From 5b541f7b9133242c9610f0f8ac2fd52dbf57b1b6 Mon Sep 17 00:00:00 2001 From: jove Date: Sat, 3 Dec 2022 22:49:15 +0000 Subject: [PATCH] Temp commit, doesn't compile --- src/action.cpp | 8 +- src/active_item_cache.cpp | 95 +- src/active_item_cache.h | 21 +- src/active_tile_data.cpp | 4 +- src/activity_actor.cpp | 125 +- src/activity_actor_definitions.h | 26 +- src/activity_handlers.cpp | 292 +-- src/activity_handlers.h | 7 +- src/activity_item_handling.cpp | 336 ++- src/advanced_inv.cpp | 82 +- src/advanced_inv.h | 3 +- src/advanced_inv_area.cpp | 24 +- src/advanced_inv_pane.cpp | 6 +- src/armor_layers.cpp | 119 +- src/avatar.cpp | 16 +- src/avatar.h | 4 +- src/avatar_action.cpp | 204 +- src/avatar_action.h | 18 +- src/ballistics.cpp | 11 +- src/basecamp.cpp | 21 +- src/basecamp.h | 4 +- src/bionics.cpp | 131 +- src/bionics_ui.cpp | 5 +- src/cata_arena.h | 49 + src/cata_tiles.cpp | 12 +- src/character.cpp | 658 +++--- src/character.h | 72 +- src/character_functions.cpp | 2 +- src/character_martial_arts.cpp | 2 +- src/character_oracle.cpp | 10 +- src/clzones.cpp | 16 +- src/colony.h | 3512 +----------------------------- src/computer_session.cpp | 45 +- src/condition.cpp | 2 +- src/construction.cpp | 35 +- src/construction_partial.h | 2 +- src/consumption.cpp | 39 +- src/craft_command.cpp | 13 +- src/craft_command.h | 2 +- src/crafting.cpp | 486 +++-- src/crafting.h | 6 +- src/crafting_gui.cpp | 15 +- src/creature.cpp | 5 +- src/debug_menu.cpp | 13 +- src/dump.cpp | 21 +- src/editmap.cpp | 16 +- src/examine_item_menu.cpp | 20 +- src/examine_item_menu.h | 2 +- src/explosion.cpp | 16 +- src/faction.cpp | 2 +- src/faction_camp.cpp | 109 +- src/fstream_utils.h | 8 + src/fungal_effects.cpp | 10 +- src/game.cpp | 166 +- src/game.h | 6 +- src/game_inventory.cpp | 363 ++- src/game_inventory.h | 50 +- src/gamemode_defense.cpp | 37 +- src/gamemode_tutorial.cpp | 14 +- src/gates.cpp | 8 +- src/handle_action.cpp | 89 +- src/handle_liquid.cpp | 36 +- src/handle_liquid.h | 4 +- src/iexamine.cpp | 608 +++--- src/iexamine.h | 5 +- src/init.h | 2 +- src/inventory.cpp | 300 +-- src/inventory.h | 47 +- src/inventory_ui.cpp | 111 +- src/inventory_ui.h | 35 +- src/item.cpp | 215 +- src/item.h | 218 +- src/item_action.cpp | 13 +- src/item_contents.cpp | 141 +- src/item_contents.h | 20 +- src/item_factory.cpp | 2 +- src/item_factory.h | 2 +- src/item_group.cpp | 146 +- src/item_group.h | 20 +- src/item_handling_util.h | 6 +- src/item_location.cpp | 20 +- src/item_location.h | 4 +- src/item_stack.cpp | 33 +- src/item_stack.h | 16 +- src/itype.cpp | 8 +- src/itype.h | 3 + src/iuse.cpp | 231 +- src/iuse_actor.cpp | 256 ++- src/iuse_actor.h | 5 +- src/json.h | 59 +- src/lightmap.cpp | 2 +- src/locations.cpp | 31 + src/locations.h | 114 + src/magic_spell_effect.cpp | 9 +- src/map.cpp | 426 ++-- src/map.h | 43 +- src/map_extras.cpp | 17 +- src/map_field.cpp | 37 +- src/mapgen.cpp | 16 +- src/martialarts.cpp | 12 +- src/material.cpp | 4 +- src/mattack_actors.cpp | 8 +- src/melee.cpp | 119 +- src/memorial_logger.cpp | 10 +- src/mission.cpp | 3 +- src/mission_companion.cpp | 82 +- src/mission_end.cpp | 2 +- src/mission_start.cpp | 4 +- src/monattack.cpp | 100 +- src/mondeath.cpp | 69 +- src/mondefense.cpp | 10 +- src/monexamine.cpp | 108 +- src/monmove.cpp | 6 +- src/monster.cpp | 90 +- src/monster.h | 35 +- src/monstergenerator.cpp | 3 +- src/morale.cpp | 6 - src/morale.h | 1 - src/mutation.cpp | 13 +- src/newcharacter.cpp | 54 +- src/npc.cpp | 292 +-- src/npc.h | 21 +- src/npcmove.cpp | 202 +- src/npctalk.cpp | 97 +- src/npctalk_funcs.cpp | 21 +- src/npctrade.cpp | 78 +- src/npctrade.h | 8 +- src/panels.cpp | 6 +- src/pickup.cpp | 124 +- src/pickup.h | 1 - src/pickup_token.h | 15 +- src/player.cpp | 236 +- src/player.h | 46 +- src/player_activity.h | 2 +- src/player_hardcoded_effects.cpp | 6 +- src/profession.cpp | 58 +- src/profession.h | 3 +- src/projectile.cpp | 22 +- src/projectile.h | 7 +- src/ranged.cpp | 12 +- src/recipe.cpp | 44 +- src/recipe.h | 6 +- src/requirements.cpp | 4 +- src/requirements.h | 3 +- src/safe_reference.cpp | 102 +- src/safe_reference.h | 361 ++- src/savegame_json.cpp | 73 +- src/start_location.cpp | 4 +- src/submap.h | 10 +- src/suffer.cpp | 32 +- src/trapfunc.cpp | 4 +- src/turret.cpp | 50 +- src/veh_interact.cpp | 71 +- src/veh_interact.h | 2 +- src/veh_utils.cpp | 5 +- src/vehicle.cpp | 119 +- src/vehicle.h | 31 +- src/vehicle_move.cpp | 2 +- src/vehicle_part.cpp | 140 +- src/vehicle_use.cpp | 66 +- src/visitable.cpp | 110 +- src/visitable.h | 17 +- 162 files changed, 5744 insertions(+), 8264 deletions(-) create mode 100644 src/cata_arena.h create mode 100644 src/locations.cpp create mode 100644 src/locations.h diff --git a/src/action.cpp b/src/action.cpp index 4ba15c46663b..1a6a89e67644 100644 --- a/src/action.cpp +++ b/src/action.cpp @@ -555,12 +555,12 @@ bool can_butcher_at( const tripoint &p ) bool has_corpse = false; const inventory &crafting_inv = you.crafting_inventory(); - for( item &items_it : items ) { - if( items_it.is_corpse() ) { + for( item *&items_it : items ) { + if( items_it->is_corpse() ) { if( factor != INT_MIN || factorD != INT_MIN ) { has_corpse = true; } - } else if( crafting::can_disassemble( you, items_it, crafting_inv ).success() ) { + } else if( crafting::can_disassemble( you, *items_it, crafting_inv ).success() ) { has_item = true; } } @@ -681,7 +681,7 @@ action_id handle_action_menu() action_weightings[ACTION_CYCLE_MOVE] = 400; } // Only prioritize fire weapon options if we're wielding a ranged weapon. - if( g->u.weapon.is_gun() || g->u.weapon.has_flag( flag_REACH_ATTACK ) ) { + if( g->u.get_weapon().is_gun() || g->u.get_weapon().has_flag( flag_REACH_ATTACK ) ) { action_weightings[ACTION_FIRE] = 350; } } diff --git a/src/active_item_cache.cpp b/src/active_item_cache.cpp index 2f24b5236bad..362725a18d02 100644 --- a/src/active_item_cache.cpp +++ b/src/active_item_cache.cpp @@ -8,47 +8,37 @@ void active_item_cache::remove( const item *it ) { - active_items[it->processing_speed()].remove_if( [it]( const item_reference & active_item ) { - item *const target = active_item.item_ref.get(); - return !target || target == it; - } ); + std::vector> &items = active_items[it->processing_speed()]; + std::remove( items.begin(), items.end(), it ); if( it->can_revive() ) { - special_items[ special_item_type::corpse ].remove_if( [it]( const item_reference & active_item ) { - item *const target = active_item.item_ref.get(); - return !target || target == it; - } ); + std::vector> &corpse = special_items[ special_item_type::corpse ]; + std::remove( corpse.begin(), corpse.end(), it ); } if( it->get_use( "explosion" ) ) { - special_items[ special_item_type::explosive ].remove_if( [it]( const item_reference & - active_item ) { - item *const target = active_item.item_ref.get(); - return !target || target == it; - } ); + std::vector> &explosive = special_items[ special_item_type::explosive ]; + std::remove( explosive.begin(), explosive.end(), it ); } } -void active_item_cache::add( item &it, point location ) +void active_item_cache::add( item &it ) { // If the item is alread in the cache for some reason, don't add a second reference - std::list &target_list = active_items[it.processing_speed()]; - if( std::find_if( target_list.begin(), - target_list.end(), [&it]( const item_reference & active_item_ref ) { - return &it == active_item_ref.item_ref.get(); - } ) != target_list.end() ) { + std::vector> &target_list = active_items[it.processing_speed()]; + if( std::find( target_list.begin(), target_list.end(), it ) != target_list.end() ) { return; } if( it.can_revive() ) { - special_items[ special_item_type::corpse ].push_back( item_reference{ location, it.get_safe_reference() } ); + special_items[ special_item_type::corpse ].push_back( it ); } if( it.get_use( "explosion" ) ) { - special_items[ special_item_type::explosive ].push_back( item_reference{ location, it.get_safe_reference() } ); + special_items[ special_item_type::explosive ].push_back( it ); } - target_list.push_back( item_reference{ location, it.get_safe_reference() } ); + target_list.push_back( it ); } bool active_item_cache::empty() const { - for( std::pair> active_queue : active_items ) { + for( std::pair>> active_queue : active_items ) { if( !active_queue.second.empty() ) { return false; } @@ -56,13 +46,13 @@ bool active_item_cache::empty() const return true; } -std::vector active_item_cache::get() +std::vector active_item_cache::get() { - std::vector all_cached_items; - for( std::pair> &kv : active_items ) { - for( std::list::iterator it = kv.second.begin(); it != kv.second.end(); ) { - if( it->item_ref ) { - all_cached_items.emplace_back( *it ); + std::vector all_cached_items; + for( std::pair>> &kv : active_items ) { + for( std::vector>::iterator it = kv.second.begin(); it != kv.second.end(); ) { + if( *it ) { + all_cached_items.push_back( & **it ); ++it; } else { it = kv.second.erase( it ); @@ -72,16 +62,21 @@ std::vector active_item_cache::get() return all_cached_items; } -std::vector active_item_cache::get_for_processing() +std::vector active_item_cache::get_for_processing() { - std::vector items_to_process; - for( std::pair> &kv : active_items ) { + std::vector items_to_process; + for( std::pair < const int, std::vector>> &kv : active_items ) { // Rely on iteration logic to make sure the number is sane. int num_to_process = kv.second.size() / kv.first; - std::list::iterator it = kv.second.begin(); + std::vector>::iterator it = kv.second.begin(); + + //TODO!: This is a bit of a hack, at the very least check the maths + //Skip the entries that were processed last turn. + it += num_to_process * ( to_turn( calendar::turn ) % kv.first ); + for( ; it != kv.second.end() && num_to_process >= 0; ) { - if( it->item_ref ) { - items_to_process.push_back( *it ); + if( *it ) { + items_to_process.push_back( & **it ); --num_to_process; ++it; } else { @@ -91,34 +86,18 @@ std::vector active_item_cache::get_for_processing() } // Rotate the returned items to the end of their list so that the items that weren't // returned this time will be first in line on the next call - kv.second.splice( kv.second.end(), kv.second, kv.second.begin(), it ); + // kv.second.splice( kv.second.end(), kv.second, kv.second.begin(), it ); } return items_to_process; } -std::vector active_item_cache::get_special( special_item_type type ) -{ - std::vector matching_items; - for( const item_reference &it : special_items[type] ) { - matching_items.push_back( it ); - } - return matching_items; -} - -void active_item_cache::subtract_locations( const point &delta ) +std::vector active_item_cache::get_special( special_item_type type ) { - for( std::pair> &pair : active_items ) { - for( item_reference &ir : pair.second ) { - ir.location -= delta; - } - } -} - -void active_item_cache::rotate_locations( int turns, const point &dim ) -{ - for( std::pair> &pair : active_items ) { - for( item_reference &ir : pair.second ) { - ir.location = ir.location.rotate( turns, dim ); + std::vector matching_items; + for( const cache_reference &it : special_items[type] ) { + if( it ) { + matching_items.push_back( &*it ); } } + return matching_items; } diff --git a/src/active_item_cache.h b/src/active_item_cache.h index 2f4ad47375bc..09e75351748b 100644 --- a/src/active_item_cache.h +++ b/src/active_item_cache.h @@ -12,12 +12,6 @@ class item; -// A struct used to uniquely identify an item within a submap or vehicle. -struct item_reference { - point location; - safe_reference item_ref; -}; - enum class special_item_type : int { none, corpse, @@ -37,8 +31,8 @@ struct hash { class active_item_cache { private: - std::unordered_map> active_items; - std::unordered_map> special_items; + std::unordered_map>> active_items; + std::unordered_map>> special_items; public: /** @@ -52,7 +46,7 @@ class active_item_cache * Adds the reference to the cache. Does nothing if the reference is already in the cache. * Relies on the fact that item::processing_speed() is a constant. */ - void add( item &it, point location ); + void add( item &it ); /** * Returns true if the cache is empty @@ -63,7 +57,7 @@ class active_item_cache * Returns a vector of all cached active item references. * Broken references are removed from the cache. */ - std::vector get(); + std::vector get(); /** * Returns the first size() / processing_speed() elements of each list, rounded up. @@ -73,15 +67,12 @@ class active_item_cache * the cache. * Relies on the fact that item::processing_speed() is a constant. */ - std::vector get_for_processing(); + std::vector get_for_processing(); /** * Returns the currently tracked list of special active items. */ - std::vector get_special( special_item_type type ); - /** Subtract delta from every item_reference's location */ - void subtract_locations( const point &delta ); - void rotate_locations( int turns, const point &dim ); + std::vector get_special( special_item_type type ); }; #endif // CATA_SRC_ACTIVE_ITEM_CACHE_H diff --git a/src/active_tile_data.cpp b/src/active_tile_data.cpp index d61c8208e633..38eedcbd3240 100644 --- a/src/active_tile_data.cpp +++ b/src/active_tile_data.cpp @@ -256,8 +256,8 @@ void charger_tile::update_internal( time_point to, const tripoint_abs_ms &p, } std::int64_t power = this->power * to_seconds( to - get_last_updated() ); // TODO: Make not a copy from map.cpp - for( item &outer : sm->get_items( p_within_sm.raw() ) ) { - outer.visit_items( [&power, &grid]( item * it ) { + for( item *&outer : sm->get_items( p_within_sm.raw() ) ) { + outer->visit_items( [&power, &grid]( item * it ) { item &n = *it; if( !n.has_flag( flag_RECHARGE ) && !n.has_flag( flag_USE_UPS ) ) { return VisitResponse::NEXT; diff --git a/src/activity_actor.cpp b/src/activity_actor.cpp index e0f7d65e4e8a..20b29e867c15 100644 --- a/src/activity_actor.cpp +++ b/src/activity_actor.cpp @@ -73,19 +73,20 @@ aim_activity_actor aim_activity_actor::use_wielded() return aim_activity_actor(); } -aim_activity_actor aim_activity_actor::use_bionic( const item &fake_gun, +aim_activity_actor aim_activity_actor::use_bionic( item &fake_gun, const units::energy &cost_per_shot ) { aim_activity_actor act = aim_activity_actor(); act.bp_cost_per_shot = cost_per_shot; - act.fake_weapon = fake_gun; + //TODO!: check ownership shenangans + act.fake_weapon = &fake_gun; return act; } -aim_activity_actor aim_activity_actor::use_mutation( const item &fake_gun ) +aim_activity_actor aim_activity_actor::use_mutation( item &fake_gun ) { aim_activity_actor act = aim_activity_actor(); - act.fake_weapon = fake_gun; + act.fake_weapon = &fake_gun; return act; } @@ -237,14 +238,13 @@ std::unique_ptr aim_activity_actor::deserialize( JsonIn &jsin ) item *aim_activity_actor::get_weapon() { - if( fake_weapon.has_value() ) { + if( fake_weapon ) { // TODO: check if the player lost relevant bionic/mutation - return &fake_weapon.value(); + return fake_weapon; } else { // Check for lost gun (e.g. yanked by zombie technician) // TODO: check that this is the same gun that was used to start aiming - item *weapon = &get_player_character().weapon; - return weapon->is_null() ? nullptr : weapon; + return &get_player_character().get_weapon(); } } @@ -278,30 +278,29 @@ bool aim_activity_actor::load_RAS_weapon() const int reload_stamina_penalty = ( sta_percent < 25 ) ? ( ( 25 - sta_percent ) * 2 ) : 0; const auto ammo_location_is_valid = [&]() -> bool { - you.ammo_location.make_dirty(); if( !you.ammo_location ) { - you.ammo_location = item_location(); return false; } if( !gun->can_reload_with( you.ammo_location->typeId() ) ) { return false; } - if( square_dist( you.pos(), you.ammo_location.position() ) > 1 ) + if( square_dist( you.pos(), you.ammo_location->position() ) > 1 ) { return false; } return true; }; item::reload_option opt = ammo_location_is_valid() ? item::reload_option( &you, weapon, - weapon, you.ammo_location ) : you.select_ammo( *gun ); + weapon, *you.ammo_location ) : you.select_ammo( *gun ); if( !opt ) { // Menu canceled return false; } const int reload_time = reload_stamina_penalty + opt.moves(); - if( !gun->reload( you, std::move( opt.ammo ), 1 ) ) { + //TOOD!: check this + if( !gun->reload( you, *opt.ammo, 1 ) ) { // Reload not allowed return false; } @@ -325,8 +324,8 @@ void aim_activity_actor::unload_RAS_weapon() int moves_before_unload = you.moves; // Note: this code works only for avatar - item_location loc = item_location( you, gun.target ); - you.unload( loc ); + //TODO!: check this can't be nullptr here + you.unload( *gun.target ); // Give back time for unloading as essentially nothing has been done. if( first_turn ) { @@ -906,49 +905,50 @@ void move_items_activity_actor::do_turn( player_activity &act, Character &who ) const tripoint dest = relative_destination + who.pos(); while( who.moves > 0 && !target_items.empty() ) { - item_location target = std::move( target_items.back() ); + safe_reference target = std::move( target_items.back() ); const int quantity = quantities.back(); target_items.pop_back(); quantities.pop_back(); if( !target ) { + //TODO!: might not be appropriate to debugmsg just because something was destroyed/unloaded debugmsg( "Lost target item of ACT_MOVE_ITEMS" ); continue; } - // Don't need to make a copy here since movement can't be canceled - item &leftovers = *target; - // Make a copy to be put in the destination location - item newit = leftovers; - // Check that we can pick it up. - if( !newit.made_of( LIQUID ) ) { - // This is for hauling across zlevels, remove when going up and down stairs - // is no longer teleportation - if( newit.is_owned_by( who, true ) ) { - newit.set_owner( who ); - } else { - continue; - } - // Handle charges, quantity == 0 means move all - if( quantity != 0 && newit.count_by_charges() ) { - newit.charges = std::min( newit.charges, quantity ); - leftovers.charges -= quantity; - } else { - leftovers.charges = 0; - } - const tripoint src = target.position(); - const int distance = src.z == dest.z ? std::max( rl_dist( src, dest ), 1 ) : 1; - who.mod_moves( -pickup::cost_to_move_item( who, newit ) * distance ); - if( to_vehicle ) { - put_into_vehicle_or_drop( who, item_drop_reason::deliberate, { newit }, dest ); - } else { - drop_on_map( who, item_drop_reason::deliberate, { newit }, dest ); - } - // If we picked up a whole stack, remove the leftover item - if( leftovers.charges <= 0 ) { - target.remove_item(); - } + if( target->made_of( LIQUID ) ) { + continue; + } + + // This is for hauling across zlevels, remove when going up and down stairs + // is no longer teleportation + if( target->is_owned_by( who, true ) ) { + target->set_owner( who ); + } else { + continue; + } + + item *newit; + + // Handle charges, quantity == 0 means move all + if( quantity != 0 && target->count_by_charges() ) { + // If it's only a partial stack move, make a copy to be put in the destination location + newit = item::spawn( *target ); + newit->charges = std::min( newit->charges, quantity ); + target->charges -= quantity; + } else { + // Otherwise just move the original + newit = &( *target ); + } + + const tripoint src = target->position(); + const int distance = src.z == dest.z ? std::max( rl_dist( src, dest ), 1 ) : 1; + who.mod_moves( -pickup::cost_to_move_item( who, *newit ) * distance ); + if( to_vehicle ) { + put_into_vehicle_or_drop( who, item_drop_reason::deliberate, { newit }, dest ); + } else { + drop_on_map( who, item_drop_reason::deliberate, { newit }, dest ); } } @@ -1170,12 +1170,18 @@ void throw_activity_actor::do_turn( player_activity &act, Character &who ) { // Make copies of relevant values since the class would // not be available after act.set_to_null() - item_location target = target_loc; + if( !target ) { + debugmsg( "Lost weapon while throwing" ); + act.set_to_null(); + return; + } + + item *it = &*target; + cata::optional blind_throw_pos = blind_throw_from_pos; // Stop the activity. Whether we will or will not throw doesn't matter. act.set_to_null(); - if( !who.is_avatar() ) { // Sanity check debugmsg( "ACT_THROW is not applicable for NPCs." ); @@ -1190,7 +1196,7 @@ void throw_activity_actor::do_turn( player_activity &act, Character &who ) who.setpos( *blind_throw_pos ); } - target_handler::trajectory trajectory = target_handler::mode_throw( *who.as_avatar(), *target, + target_handler::trajectory trajectory = target_handler::mode_throw( *who.as_avatar(), *it, blind_throw_pos.has_value() ); // If we previously shifted our position, put ourselves back now that we've picked our target. @@ -1202,27 +1208,26 @@ void throw_activity_actor::do_turn( player_activity &act, Character &who ) return; } - if( &*target != &who.weapon ) { + if( it != &who.get_weapon() ) { // This is to represent "implicit offhand wielding" - int extra_cost = who.item_handling_cost( *target, true, INVENTORY_HANDLING_PENALTY / 2 ); + int extra_cost = who.item_handling_cost( *it, true, INVENTORY_HANDLING_PENALTY / 2 ); who.mod_moves( -extra_cost ); } - - item thrown = *target.get_item(); + //TODO!:check if( target->count_by_charges() && target->charges > 1 ) { target->mod_charges( -1 ); - thrown.charges = 1; + target->charges = 1; } else { - target.remove_item(); + target->detach(); } - who.as_player()->throw_item( trajectory.back(), thrown, blind_throw_pos ); + who.as_player()->throw_item( trajectory.back(), *target, blind_throw_pos ); } void throw_activity_actor::serialize( JsonOut &jsout ) const { jsout.start_object(); - jsout.member( "target_loc", target_loc ); + jsout.member( "target_loc", target ); jsout.member( "blind_throw_from_pos", blind_throw_from_pos ); jsout.end_object(); @@ -1234,7 +1239,7 @@ std::unique_ptr throw_activity_actor::deserialize( JsonIn &jsin JsonObject data = jsin.get_object(); - data.read( "target_loc", actor.target_loc ); + data.read( "target_loc", actor.target ); data.read( "blind_throw_from_pos", actor.blind_throw_from_pos ); return actor.clone(); diff --git a/src/activity_actor_definitions.h b/src/activity_actor_definitions.h index dc486221e5e4..2890d4dd6d58 100644 --- a/src/activity_actor_definitions.h +++ b/src/activity_actor_definitions.h @@ -6,7 +6,6 @@ #include "coordinates.h" #include "item_handling_util.h" -#include "item_location.h" #include "memory_fast.h" #include "optional.h" #include "pickup_token.h" @@ -20,7 +19,7 @@ class vehicle; class aim_activity_actor : public activity_actor { private: - cata::optional fake_weapon; + item *fake_weapon; units::energy bp_cost_per_shot = 0_J; int stamina_cost_per_shot = 0; std::vector fin_trajectory; @@ -56,10 +55,10 @@ class aim_activity_actor : public activity_actor static aim_activity_actor use_wielded(); /** Aiming fake gun provided by a bionic */ - static aim_activity_actor use_bionic( const item &fake_gun, const units::energy &cost_per_shot ); + static aim_activity_actor use_bionic( item &fake_gun, const units::energy &cost_per_shot ); /** Aiming fake gun provided by a mutation */ - static aim_activity_actor use_mutation( const item &fake_gun ); + static aim_activity_actor use_mutation( item &fake_gun ); activity_id get_type() const override { return activity_id( "ACT_AIM" ); @@ -346,16 +345,21 @@ class migration_cancel_activity_actor : public activity_actor class move_items_activity_actor : public activity_actor { private: - std::vector target_items; + std::vector> target_items; std::vector quantities; bool to_vehicle; tripoint relative_destination; public: - move_items_activity_actor( std::vector target_items, std::vector quantities, + move_items_activity_actor( std::vector items, std::vector quantities, bool to_vehicle, tripoint relative_destination ) : - target_items( target_items ), quantities( quantities ), to_vehicle( to_vehicle ), - relative_destination( relative_destination ) {} + quantities( quantities ), to_vehicle( to_vehicle ), + relative_destination( relative_destination ) { + + for( item *&it : items ) { + target_items.push_back( it ); + } + } activity_id get_type() const override { return activity_id( "ACT_MOVE_ITEMS" ); @@ -473,15 +477,15 @@ class stash_activity_actor : public activity_actor class throw_activity_actor : public activity_actor { private: - item_location target_loc; + safe_reference target; cata::optional blind_throw_from_pos; public: throw_activity_actor() = default; throw_activity_actor( - item_location target_loc, + item &target, cata::optional blind_throw_from_pos - ) : target_loc( target_loc ), + ) : target( &target ), blind_throw_from_pos( blind_throw_from_pos ) {} ~throw_activity_actor() = default; diff --git a/src/activity_handlers.cpp b/src/activity_handlers.cpp index 0ffebed06a6b..c6c152d686d4 100644 --- a/src/activity_handlers.cpp +++ b/src/activity_handlers.cpp @@ -473,41 +473,41 @@ static bool check_butcher_cbm( const int roll ) return !failed; } -static void extract_or_wreck_cbms( const std::list &cbms, int roll, +static void extract_or_wreck_cbms( const ItemList &cbms, int roll, player &p ) { if( roll < 0 ) { return; } - for( item it : cbms ) { + for( item * const &it : cbms ) { // For some stupid reason, zombie pheromones are dropped using bionic type // This complicates things - if( it.is_bionic() ) { - if( check_butcher_cbm( roll ) || it.typeId() == itype_burnt_out_bionic ) { - add_msg( m_good, _( "You discover a %s!" ), it.tname() ); + if( it->is_bionic() ) { + if( check_butcher_cbm( roll ) || it->typeId() == itype_burnt_out_bionic ) { + add_msg( m_good, _( "You discover a %s!" ), it->tname() ); } else { // We convert instead of recreating so that it keeps flags and faults - it.convert( itype_burnt_out_bionic ); - add_msg( m_bad, _( "Your imprecise surgery damaged a bionic, producing a %s." ), it.tname() ); + it->convert( itype_burnt_out_bionic ); + add_msg( m_bad, _( "Your imprecise surgery damaged a bionic, producing a %s." ), it->tname() ); } } else { if( !check_butcher_cbm( roll ) ) { add_msg( m_bad, _( "Your imprecise surgery destroyed some organs." ) ); continue; } else { - add_msg( m_good, _( "You discover a %s!" ), it.tname() ); + add_msg( m_good, _( "You discover a %s!" ), it->tname() ); } } - if( it.type->phase == LIQUID ) { + if( it->type->phase == LIQUID ) { // TODO: smarter NPC liquid handling if( p.is_npc() ) { drop_on_map( p, item_drop_reason::deliberate, { it }, p.pos() ); } else { - liquid_handler::handle_all_liquid( it, 1 ); + liquid_handler::handle_all_liquid( *it, 1 ); } } else { - get_map().add_item( p.pos(), it ); + get_map().add_item( p.pos(), *it ); } } } @@ -1053,7 +1053,7 @@ static void butchery_drops_harvest( item *corpse_item, const mtype &mt, player & continue; } if( drop->phase == LIQUID ) { - item obj( drop, calendar::turn, roll ); + item &obj = *item::spawn( drop, calendar::turn, roll ); if( obj.goes_bad() ) { obj.set_rot( corpse_item->get_rot() ); } @@ -1065,12 +1065,12 @@ static void butchery_drops_harvest( item *corpse_item, const mtype &mt, player & } // TODO: smarter NPC liquid handling if( p.is_npc() ) { - drop_on_map( p, item_drop_reason::deliberate, { obj }, p.pos() ); + drop_on_map( p, item_drop_reason::deliberate, { &obj }, p.pos() ); } else { liquid_handler::handle_all_liquid( obj, 1 ); } } else if( drop->count_by_charges() ) { - item obj( drop, calendar::turn, roll ); + item &obj = *item::spawn( drop, calendar::turn, roll ); if( obj.goes_bad() ) { obj.set_rot( corpse_item->get_rot() ); } @@ -1085,7 +1085,7 @@ static void butchery_drops_harvest( item *corpse_item, const mtype &mt, player & } here.add_item_or_charges( p.pos(), obj ); } else { - item obj( drop, calendar::turn ); + item &obj = *item::spawn( drop, calendar::turn ); obj.set_mtype( &mt ); if( obj.goes_bad() ) { obj.set_rot( corpse_item->get_rot() ); @@ -1100,8 +1100,9 @@ static void butchery_drops_harvest( item *corpse_item, const mtype &mt, player & obj.set_var( "activity_var", p.name ); } for( int i = 0; i != roll; ++i ) { - here.add_item_or_charges( p.pos(), obj ); + here.add_item_or_charges( p.pos(), *item::spawn( obj ) ); } + obj.destroy(); } p.add_msg_if_player( m_good, _( "You harvest: %s" ), drop->nname( roll ) ); } @@ -1137,7 +1138,7 @@ void activity_handlers::butcher_finish( player_activity *act, player *p ) } map &here = get_map(); - item_location target = act->targets.back(); + safe_reference &target = act->targets.back(); const inventory &inv = p->crafting_inventory(); // Corpses can disappear (rezzing!), so check for that @@ -1230,7 +1231,9 @@ void activity_handlers::butcher_finish( player_activity *act, player *p ) } // Remove the target from the map - target.remove_item(); + target->detach(); + target->destroy(); + act->targets.pop_back(); here.add_splatter( type_gib, p->pos(), rng( corpse->size + 2, ( corpse->size + 1 ) * 2 ) ); @@ -1261,9 +1264,9 @@ void activity_handlers::butcher_finish( player_activity *act, player *p ) int roll = roll_butchery() - corpse_item.damage_level( 4 ); roll = roll < 0 ? 0 : roll; add_msg( m_debug, _( "Roll penalty for corpse damage = %s" ), 0 - corpse_item.damage_level( 4 ) ); - std::list cbms = corpse_item.components; - for( const item *it : corpse_item.contents.all_items_top() ) { - cbms.push_back( *it ); + ItemList cbms = corpse_item.components; + for( item *it : corpse_item.contents.all_items_top() ) { + cbms.push_back( it ); } extract_or_wreck_cbms( cbms, roll, *p ); // those lines are for XP gain with dissecting. It depends on the size of the corpse, time to dissect the corpse and the amount of bionics you would gather. @@ -1287,7 +1290,8 @@ void activity_handlers::butcher_finish( player_activity *act, player *p ) corpse_item.tname() ); // Remove the target from the map - target.remove_item(); + target->detach(); + target->destroy(); if( !act->targets.empty() ) { act->targets.pop_back(); } @@ -1296,7 +1300,8 @@ void activity_handlers::butcher_finish( player_activity *act, player *p ) p->add_msg_if_player( m_good, _( "You finish butchering the %s." ), corpse_item.tname() ); // Remove the target from the map - target.remove_item(); + target->detach(); + target->destroy(); if( !act->targets.empty() ) { act->targets.pop_back(); } @@ -1397,7 +1402,8 @@ void activity_handlers::butcher_finish( player_activity *act, player *p ) } // Remove the target from the map - target.remove_item(); + target->detach(); + target->destroy(); if( !act->targets.empty() ) { act->targets.pop_back(); } @@ -1406,7 +1412,8 @@ void activity_handlers::butcher_finish( player_activity *act, player *p ) p->add_msg_if_player( m_good, _( "You finish dissecting the %s." ), corpse_item.tname() ); // Remove the target from the map - target.remove_item(); + target->detach(); + target->destroy(); if( !act->targets.empty() ) { act->targets.pop_back(); } @@ -1425,12 +1432,13 @@ void activity_handlers::shear_finish( player_activity *act, player *p ) debugmsg( "shearing activity with no position of monster stored" ); return; } - item_location &loc = act->targets[ 0 ]; - item *shears = loc.get_item(); - if( shears == nullptr ) { + safe_reference &loc = act->targets[ 0 ]; + + if( !loc ) { debugmsg( "shearing item location lost" ); return; } + item *shears = &*loc; map &here = get_map(); const tripoint source_pos = here.getlocal( act->coords.at( 0 ) ); monster *source_mon = g->critter_at( source_pos ); @@ -1440,7 +1448,7 @@ void activity_handlers::shear_finish( player_activity *act, player *p ) } // 22 wool staples corresponds to an average wool-producing sheep yield of 10 lbs or so for( int i = 0; i != 22; ++i ) { - item wool_staple( itype_wool_staple, calendar::turn ); + item &wool_staple = *item::spawn( itype_wool_staple, calendar::turn ); here.add_item_or_charges( p->pos(), wool_staple ); } source_mon->add_effect( effect_sheared, calendar::season_length() ); @@ -1475,7 +1483,7 @@ void activity_handlers::milk_finish( player_activity *act, player *p ) debugmsg( "started milking but udders are now empty before milking finishes" ); return; } - item milk( milked_item->first, calendar::turn, milked_item->second ); + item &milk = *item::spawn( milked_item->first, calendar::turn, milked_item->second ); if( liquid_handler::handle_liquid( milk, nullptr, 1, nullptr, nullptr, -1, source_mon ) ) { milked_item->second = 0; if( milk.charges > 0 ) { @@ -1491,7 +1499,7 @@ void activity_handlers::milk_finish( player_activity *act, player *p ) } act->set_to_null(); } - +//TODO!: check creation behavior void activity_handlers::fill_liquid_do_turn( player_activity *act, player *p ) { player_activity &act_ref = *act; @@ -1503,7 +1511,8 @@ void activity_handlers::fill_liquid_do_turn( player_activity *act, player *p ) map_stack source_stack = here.i_at( source_pos ); map_stack::iterator on_ground; monster *source_mon = nullptr; - item liquid; + //TODO!: check what in the fucking fuck is going on here + item *liquid; const liquid_source_type source_type = static_cast( act_ref.values.at( 0 ) ); int part_num = -1; int veh_charges = 0; @@ -1515,11 +1524,11 @@ void activity_handlers::fill_liquid_do_turn( player_activity *act, player *p ) } deserialize( liquid, act_ref.str_values.at( 0 ) ); part_num = static_cast( act_ref.values.at( 1 ) ); - veh_charges = liquid.charges; + veh_charges = liquid->charges; break; case LST_INFINITE_MAP: deserialize( liquid, act_ref.str_values.at( 0 ) ); - liquid.charges = item::INFINITE_CHARGES; + liquid->charges = item::INFINITE_CHARGES; break; case LST_MAP_ITEM: if( static_cast( act_ref.values.at( 1 ) ) >= source_stack.size() ) { @@ -1537,42 +1546,42 @@ void activity_handlers::fill_liquid_do_turn( player_activity *act, player *p ) act_ref.set_to_null(); } deserialize( liquid, act_ref.str_values.at( 0 ) ); - liquid.charges = 1; + liquid->charges = 1; break; } static const units::volume volume_per_second = units::from_liter( 4.0F / 6.0F ); - const int charges_per_second = std::max( 1, liquid.charges_per_volume( volume_per_second ) ); - liquid.charges = std::min( charges_per_second, liquid.charges ); - const int original_charges = liquid.charges; + const int charges_per_second = std::max( 1, liquid->charges_per_volume( volume_per_second ) ); + liquid->charges = std::min( charges_per_second, liquid->charges ); + const int original_charges = liquid->charges; // 2. Transfer charges. switch( static_cast( act_ref.values.at( 2 ) ) ) { case LTT_VEHICLE: if( const optional_vpart_position vp = here.veh_at( act_ref.coords.at( 1 ) ) ) { - p->pour_into( vp->vehicle(), liquid ); + p->pour_into( vp->vehicle(), *liquid ); } else { throw std::runtime_error( "could not find target vehicle for liquid transfer" ); } break; case LTT_CONTAINER: - p->pour_into( p->i_at( act_ref.values.at( 3 ) ), liquid ); + p->pour_into( p->i_at( act_ref.values.at( 3 ) ), *liquid ); break; case LTT_MAP: if( iexamine::has_keg( act_ref.coords.at( 1 ) ) ) { - iexamine::pour_into_keg( act_ref.coords.at( 1 ), liquid ); + iexamine::pour_into_keg( act_ref.coords.at( 1 ), *liquid ); } else { - here.add_item_or_charges( act_ref.coords.at( 1 ), liquid ); - p->add_msg_if_player( _( "You pour %1$s onto the ground." ), liquid.tname() ); - liquid.charges = 0; + here.add_item_or_charges( act_ref.coords.at( 1 ), *liquid ); + p->add_msg_if_player( _( "You pour %1$s onto the ground." ), liquid->tname() ); + liquid->charges = 0; } break; case LTT_MONSTER: - liquid.charges = 0; + liquid->charges = 0; break; } - const int removed_charges = original_charges - liquid.charges; + const int removed_charges = original_charges - liquid->charges; if( removed_charges == 0 ) { // Nothing has been transferred, target must be full. act_ref.set_to_null(); @@ -1584,36 +1593,36 @@ void activity_handlers::fill_liquid_do_turn( player_activity *act, player *p ) case LST_VEHICLE: if( part_num != -1 ) { source_veh->drain( part_num, removed_charges ); - liquid.charges = veh_charges - removed_charges; + liquid->charges = veh_charges - removed_charges; // If there's no liquid left in this tank we're done, otherwise // we need to update our liquid serialization to reflect how // many charges are actually left for the next time we come // around this loop. - if( !liquid.charges ) { + if( !liquid->charges ) { act_ref.set_to_null(); } else { if( act_ref.str_values.empty() ) { act_ref.str_values.push_back( std::string() ); } - act_ref.str_values.at( 0 ) = serialize( liquid ); + act_ref.str_values.at( 0 ) = serialize( *liquid ); } } else { - source_veh->drain( liquid.typeId(), removed_charges ); + source_veh->drain( liquid->typeId(), removed_charges ); } - if( source_veh->fuel_left( liquid.typeId() ) <= 0 ) { + if( source_veh->fuel_left( liquid->typeId() ) <= 0 ) { act_ref.set_to_null(); } break; case LST_MAP_ITEM: - on_ground->charges -= removed_charges; - if( on_ground->charges <= 0 ) { + ( *on_ground )->charges -= removed_charges; + if( ( *on_ground )->charges <= 0 ) { source_stack.erase( on_ground ); if( here.ter( source_pos ).obj().examine == &iexamine::gaspump ) { add_msg( _( "With a clang and a shudder, the %s pump goes silent." ), - liquid.type_name( 1 ) ); + liquid->type_name( 1 ) ); } else if( here.furn( source_pos ).obj().examine == &iexamine::fvat_full ) { add_msg( _( "You squeeze the last drops of %s from the vat." ), - liquid.type_name( 1 ) ); + liquid->type_name( 1 ) ); map_stack items_here = here.i_at( source_pos ); if( items_here.empty() ) { here.furn_set( source_pos, f_fvat_empty ); @@ -1627,7 +1636,7 @@ void activity_handlers::fill_liquid_do_turn( player_activity *act, player *p ) break; case LST_MONSTER: // liquid source charges handled in monexamine::milk_source - if( liquid.charges == 0 ) { + if( liquid->charges == 0 ) { act_ref.set_to_null(); } break; @@ -1843,10 +1852,9 @@ void activity_handlers::longsalvage_finish( player_activity *act, player *p ) return; } - for( item &it : items ) { - if( actor->valid_to_cut_up( it ) ) { - item_location item_loc( map_cursor( p->pos() ), &it ); - actor->cut_up( *p, *salvage_tool, item_loc ); + for( item *&it : items ) { + if( actor->valid_to_cut_up( *it ) ) { + actor->cut_up( *p, *salvage_tool, *it ); return; } } @@ -1862,9 +1870,9 @@ void activity_handlers::make_zlave_finish( player_activity *act, player *p ) const std::string corpse_name = act->str_values[0]; item *body = nullptr; - for( item &it : items ) { - if( it.display_name() == corpse_name ) { - body = ⁢ + for( item *&it : items ) { + if( it->display_name() == corpse_name ) { + body = it; } } @@ -1972,8 +1980,8 @@ void activity_handlers::pickaxe_finish( player_activity *act, player *p ) debugmsg( "pickaxe activity targets empty" ); } if( resume_for_multi_activities( *p ) ) { - for( item &elem : here.i_at( pos ) ) { - elem.set_var( "activity_var", p->name ); + for( item *&elem : here.i_at( pos ) ) { + elem->set_var( "activity_var", p->name ); } } } @@ -1984,48 +1992,48 @@ void activity_handlers::pulp_do_turn( player_activity *act, player *p ) const tripoint &pos = here.getlocal( act->placement ); // Stabbing weapons are a lot less effective at pulping - const int cut_power = std::max( p->weapon.damage_melee( DT_CUT ), - p->weapon.damage_melee( DT_STAB ) / 2 ); + const int cut_power = std::max( p->get_weapon().damage_melee( DT_CUT ), + p->get_weapon().damage_melee( DT_STAB ) / 2 ); ///\EFFECT_STR increases pulping power, with diminishing returns - float pulp_power = std::sqrt( ( p->str_cur + p->weapon.damage_melee( DT_BASH ) ) * + float pulp_power = std::sqrt( ( p->str_cur + p->get_weapon().damage_melee( DT_BASH ) ) * ( cut_power + 1.0f ) ); - float pulp_effort = p->str_cur + p->weapon.damage_melee( DT_BASH ); + float pulp_effort = p->str_cur + p->get_weapon().damage_melee( DT_BASH ); // Multiplier to get the chance right + some bonus for survival skill pulp_power *= 40 + p->get_skill_level( skill_survival ) * 5; - const int mess_radius = p->weapon.has_flag( flag_MESSY ) ? 2 : 1; + const int mess_radius = p->get_weapon().has_flag( flag_MESSY ) ? 2 : 1; int moves = 0; // use this to collect how many corpse are pulped int &num_corpses = act->index; map_stack corpse_pile = here.i_at( pos ); - for( item &corpse : corpse_pile ) { - const mtype *corpse_mtype = corpse.get_mtype(); - if( !corpse.is_corpse() || !corpse_mtype->has_flag( MF_REVIVES ) || + for( item *&corpse : corpse_pile ) { + const mtype *corpse_mtype = corpse->get_mtype(); + if( !corpse->is_corpse() || !corpse_mtype->has_flag( MF_REVIVES ) || ( std::find( act->str_values.begin(), act->str_values.end(), "auto_pulp_no_acid" ) != act->str_values.end() && corpse_mtype->bloodType().obj().has_acid ) ) { // Don't smash non-rezing corpses //don't smash acid zombies when auto pulping continue; } - while( corpse.damage() < corpse.max_damage() ) { + while( corpse->damage() < corpse->max_damage() ) { // Increase damage as we keep smashing ensuring we eventually smash the target. - if( x_in_y( pulp_power, corpse.volume() / units::legacy_volume_factor ) ) { - corpse.inc_damage( DT_BASH ); - if( corpse.damage() == corpse.max_damage() ) { + if( x_in_y( pulp_power, corpse->volume() / units::legacy_volume_factor ) ) { + corpse->inc_damage( DT_BASH ); + if( corpse->damage() == corpse->max_damage() ) { num_corpses++; } } - if( x_in_y( pulp_power, corpse.volume() / units::legacy_volume_factor ) ) { + if( x_in_y( pulp_power, corpse->volume() / units::legacy_volume_factor ) ) { // Splatter some blood around // Splatter a bit more randomly, so that it looks cooler const int radius = mess_radius + x_in_y( pulp_power, 500 ) + x_in_y( pulp_power, 1000 ); const tripoint dest( pos + point( rng( -radius, radius ), rng( -radius, radius ) ) ); const field_type_id type_blood = ( mess_radius > 1 && x_in_y( pulp_power, 10000 ) ) ? - corpse.get_mtype()->gibType() : - corpse.get_mtype()->bloodType(); + corpse->get_mtype()->gibType() : + corpse->get_mtype()->bloodType(); here.add_splatter_trail( type_blood, pos, dest ); } @@ -2048,7 +2056,7 @@ void activity_handlers::pulp_do_turn( player_activity *act, player *p ) return; } } - corpse.set_flag( flag_PULPED ); + corpse->set_flag( flag_PULPED ); } // If we reach this, all corpses have been pulped, finish the activity act->moves_left = 0; @@ -2099,7 +2107,7 @@ void activity_handlers::reload_finish( player_activity *act, player *p ) const bool is_speedloader = ammo.has_flag( flag_SPEEDLOADER ); const bool ammo_is_filthy = ammo.is_filthy(); - if( !reloadable.reload( *p, std::move( act->targets[ 1 ] ), qty ) ) { + if( !reloadable.reload( *p, ammo, qty ) ) { add_msg( m_info, _( "Can't reload the %s." ), reloadable.tname() ); return; } @@ -2519,13 +2527,13 @@ void activity_handlers::cracking_finish( player_activity *act, player *p ) void activity_handlers::lockpicking_finish( player_activity *act, player *p ) { - item_location &loc = act->targets[ 0 ]; - item *it = loc.get_item(); - if( it == nullptr ) { + safe_reference &loc = act->targets[ 0 ]; + + if( !loc ) { debugmsg( "lockpick item location lost" ); return; } - + item *it = &*loc; const ter_id ter_type = g->m.ter( act->placement ); const furn_id furn_type = g->m.furn( act->placement ); lockpicking_open_result lr = get_lockpicking_open_result( ter_type, furn_type ); @@ -2656,12 +2664,12 @@ tripoint get_position( const player_activity &activity ) return activity.coords.at( 0 ); } -item get_fake_tool( hack_type_t hack_type, const player_activity &activity ) +item *get_fake_tool( hack_type_t hack_type, const player_activity &activity ) { const tripoint position = get_position( activity ); const map &m = get_map(); - - item fake_item; + //TODO!: chhhecks of big + item *fake_item = &null_item_reference(); switch( hack_type ) { case hack_type_t::vehicle_weldrig: { @@ -2672,8 +2680,8 @@ item get_fake_tool( hack_type_t hack_type, const player_activity &activity ) } const vehicle &veh = pos->vehicle(); - fake_item = item( itype_welder, calendar::turn, 0 ); - fake_item.charges = veh.fuel_left( itype_battery ); + fake_item = item::spawn( itype_welder, calendar::turn, 0 ); + fake_item->charges = veh.fuel_left( itype_battery ); break; } @@ -2698,8 +2706,8 @@ item get_fake_tool( hack_type_t hack_type, const player_activity &activity ) } const tripoint_abs_ms abspos( m.getabs( position ) ); const distribution_grid &grid = get_distribution_grid_tracker().grid_at( abspos ); - fake_item = item( item_type.get_id(), calendar::turn, 0 ); - fake_item.charges = grid.get_resource( true ); + fake_item = item::spawn( item_type.get_id(), calendar::turn, 0 ); + fake_item->charges = grid.get_resource( true ); break; } } @@ -2707,7 +2715,7 @@ item get_fake_tool( hack_type_t hack_type, const player_activity &activity ) } } - fake_item.set_flag( "PSEUDO" ); + fake_item->set_flag( "PSEUDO" ); return fake_item; } @@ -2814,14 +2822,14 @@ void activity_handlers::repair_item_finish( player_activity *act, player *p ) const std::string iuse_name_string = act->get_str_value( 0, "repair_item" ); repeat_type repeat = static_cast( act->get_value( 0, REPEAT_INIT ) ); - item_location *ploc = nullptr; - if( !act->targets.empty() ) { - ploc = &act->targets[0]; + item *ploc = nullptr; + if( !act->targets.empty() && act->targets[0] ) { + ploc = &*act->targets[0]; } // nullopt if used real tool cata::optional hack_type = hack::get_hack_type( *act ); - cata::optional fake_tool = cata::nullopt; + item *fake_tool = nullptr; if( hack_type ) { fake_tool = hack::get_fake_tool( hack_type.value(), *act ); } @@ -2830,10 +2838,10 @@ void activity_handlers::repair_item_finish( player_activity *act, player *p ) item *main_tool = nullptr; if( hack_type.has_value() ) { - main_tool = &fake_tool.value(); + main_tool = fake_tool; } if( main_tool == nullptr && ploc ) { - main_tool = & **ploc; + main_tool = ploc; } if( main_tool == nullptr ) { main_tool = &p->i_at( act->index ); @@ -2863,13 +2871,13 @@ void activity_handlers::repair_item_finish( player_activity *act, player *p ) // Valid Repeat choice and target, attempt repair. if( repeat != REPEAT_INIT && act->targets.size() >= 2 ) { - item_location &fix_location = act->targets[1]; + safe_reference &fix_location = act->targets[1]; // Remember our level: we want to stop retrying on level up const int old_level = p->get_skill_level( actor->used_skill ); - const repair_item_actor::attempt_hint attempt = actor->repair( *p, *used_tool, fix_location ); + const repair_item_actor::attempt_hint attempt = actor->repair( *p, *used_tool, *fix_location ); if( attempt != repair_item_actor::AS_CANT ) { - if( ploc && ploc->where() == item_location::type::map ) { + if( ploc && ploc->where() == item::item_location_type::map ) { used_tool->ammo_consume( used_tool->ammo_required(), ploc->position() ); } else { p->consume_charges( *used_tool, used_tool->ammo_required() ); @@ -2924,9 +2932,9 @@ void activity_handlers::repair_item_finish( player_activity *act, player *p ) // target selection and validation. while( act->targets.size() < 2 ) { - item_location item_loc = game_menus::inv::repair( *p, actor, main_tool ); + item *item_loc = game_menus::inv::repair( *p, actor, main_tool ); - if( item_loc == item_location::nowhere ) { + if( item_loc == nullptr ) { p->add_msg_if_player( m_info, _( "Never mind." ) ); act->set_to_null(); return; @@ -3000,7 +3008,7 @@ void activity_handlers::mend_item_finish( player_activity *act, player *p ) return; } - item_location &target = act->targets[ 0 ]; + item *target = &*act->targets[ 0 ]; const auto f = target->faults.find( fault_id( act->name ) ); if( f == target->faults.end() ) { @@ -3111,11 +3119,11 @@ void activity_handlers::toolmod_add_finish( player_activity *act, player *p ) } item &tool = *act->targets[0]; item &mod = *act->targets[1]; + mod.detach(); p->add_msg_if_player( m_good, _( "You successfully attached the %1$s to your %2$s." ), mod.tname(), tool.tname() ); mod.set_flag( "IRREMOVABLE" ); tool.put_in( mod ); - act->targets[1].remove_item(); } void activity_handlers::clear_rubble_finish( player_activity *act, player *p ) @@ -3255,9 +3263,9 @@ static void rod_fish( player *p, const std::vector &fishables ) p->add_msg_if_player( m_good, _( "You caught a %s." ), chosen_fish->type->nname() ); } } - for( item &elem : here.i_at( p->pos() ) ) { - if( elem.is_corpse() && !elem.has_var( "activity_var" ) ) { - elem.set_var( "activity_var", p->name ); + for( item *&elem : here.i_at( p->pos() ) ) { + if( elem->is_corpse() && !elem->has_var( "activity_var" ) ) { + elem->set_var( "activity_var", p->name ); } } } @@ -3310,8 +3318,10 @@ void activity_handlers::fish_finish( player_activity *act, player *p ) void activity_handlers::cracking_do_turn( player_activity *act, player *p ) { auto cracking_tool = p->crafting_inventory().items_with( []( const item & it ) -> bool { - item temporary_item( it.type ); - return temporary_item.has_flag( flag_SAFECRACK ); + //TODO!: Whhhyyyy + item *temporary_item = item::spawn( it.type ); + temporary_item->destroy(); + return temporary_item->has_flag( flag_SAFECRACK ); } ); if( cracking_tool.empty() && !p->has_bionic( bio_ears ) ) { // We lost our cracking tool somehow, bail out. @@ -3354,7 +3364,7 @@ void activity_handlers::read_do_turn( player_activity *act, player *p ) } if( calendar::once_every( 1_minutes ) ) { - item_location loc = act->targets[0]; + safe_reference &loc = act->targets[0]; if( !loc || !loc->is_book() ) { p->add_msg_if_player( m_bad, _( "You lost your book! You stop reading." ) ); act->set_to_null(); @@ -3370,10 +3380,10 @@ void activity_handlers::read_finish( player_activity *act, player *p ) } if( p->is_npc() ) { npc *guy = dynamic_cast( p ); - guy->finish_read( act->targets.front() ); + guy->finish_read( &*act->targets.front() ); } else { if( avatar *u = dynamic_cast( p ) ) { - u->do_read( act->targets.front() ); + u->do_read( &*act->targets.front() ); } else { act->set_to_null(); } @@ -3762,19 +3772,19 @@ void activity_handlers::plant_seed_finish( player_activity *act, player *p ) map &here = get_map(); tripoint examp = here.getlocal( act->placement ); const itype_id seed_id( act->str_values[0] ); - std::list used_seed; + ItemList used_seed; if( item::count_by_charges( seed_id ) ) { used_seed = p->use_charges( seed_id, 1 ); } else { used_seed = p->use_amount( seed_id, 1 ); } if( !used_seed.empty() ) { - used_seed.front().set_age( 0_turns ); - if( used_seed.front().has_var( "activity_var" ) ) { - used_seed.front().erase_var( "activity_var" ); + used_seed.front()->set_age( 0_turns ); + if( used_seed.front()->has_var( "activity_var" ) ) { + used_seed.front()->erase_var( "activity_var" ); } - used_seed.front().set_flag( flag_HIDDEN_ITEM ); - here.add_item_or_charges( examp, used_seed.front() ); + used_seed.front()->set_flag( flag_HIDDEN_ITEM ); + here.add_item_or_charges( examp, *used_seed.front() ); if( here.has_flag_furn( flag_PLANTABLE, examp ) ) { here.furn_set( examp, furn_str_id( here.furn( examp )->plant->transform ) ); } else { @@ -3901,7 +3911,7 @@ void activity_handlers::fetch_do_turn( player_activity *act, player *p ) void activity_handlers::craft_do_turn( player_activity *act, player *p ) { - item *craft = act->targets.front().get_item(); + item *craft = &*act->targets.front(); // item_location::get_item() will return nullptr if the item is lost if( !craft ) { @@ -3983,12 +3993,14 @@ void activity_handlers::craft_do_turn( player_activity *act, player *p ) // if item_counter has reached 100% or more if( craft->item_counter >= 10'000'000 ) { - item craft_copy = *craft; - act->targets.front().remove_item(); + //TODO!: CHEEKY check + item *craft_copy = craft; + act->targets.front()->detach(); + act->targets.front()->destroy(); p->cancel_activity(); - complete_craft( *p, craft_copy, bench_location{bench_t, bench_pos} ); + complete_craft( *p, *craft_copy, bench_location{bench_t, bench_pos} ); if( is_long ) { - if( p->making_would_work( p->lastrecipe, craft_copy.charges ) ) { + if( p->making_would_work( p->lastrecipe, craft_copy->charges ) ) { p->last_craft->execute( bench_pos ); } } @@ -3998,7 +4010,8 @@ void activity_handlers::craft_do_turn( player_activity *act, player *p ) if( destroy ) { p->add_msg_player_or_npc( _( "There is nothing left of the %s to craft from." ), _( "There is nothing left of the %s was crafting." ), craft->tname() ); - act->targets.front().remove_item(); + act->targets.front()->detach(); + act->targets.front()->destroy(); p->cancel_activity(); } } @@ -4236,19 +4249,19 @@ void activity_handlers::chop_logs_finish( player_activity *act, player *p ) splint_quan = 0; } for( int i = 0; i != log_quan; ++i ) { - item obj( itype_log, calendar::turn ); - obj.set_var( "activity_var", p->name ); - here.add_item_or_charges( pos, obj ); + item *obj = item::spawn( itype_log, calendar::turn ); + obj->set_var( "activity_var", p->name ); + here.add_item_or_charges( pos, *obj ); } for( int i = 0; i != stick_quan; ++i ) { - item obj( itype_stick_long, calendar::turn ); - obj.set_var( "activity_var", p->name ); - here.add_item_or_charges( pos, obj ); + item *obj = item::spawn( itype_stick_long, calendar::turn ); + obj->set_var( "activity_var", p->name ); + here.add_item_or_charges( pos, *obj ); } for( int i = 0; i != splint_quan; ++i ) { - item obj( itype_splinter, calendar::turn ); - obj.set_var( "activity_var", p->name ); - here.add_item_or_charges( pos, obj ); + item *obj = item::spawn( itype_splinter, calendar::turn ); + obj->set_var( "activity_var", p->name ); + here.add_item_or_charges( pos, *obj ); } here.ter_set( pos, t_dirt ); const int helpersize = p->get_crafting_helpers( 3 ).size(); @@ -4322,8 +4335,8 @@ void activity_handlers::jackhammer_finish( player_activity *act, player *p ) debugmsg( "jackhammer activity targets empty" ); } if( resume_for_multi_activities( *p ) ) { - for( item &elem : here.i_at( pos ) ) { - elem.set_var( "activity_var", p->name ); + for( item *&elem : here.i_at( pos ) ) { + elem->set_var( "activity_var", p->name ); } } } @@ -4843,5 +4856,6 @@ void activity_handlers::mind_splicer_finish( player_activity *act, player *p ) p->add_msg_if_player( m_info, _( "…you finally find the memory banks." ) ); p->add_msg_if_player( m_info, _( "The kit makes a copy of the data inside the bionic." ) ); data_card.contents.clear_items(); - data_card.put_in( item( itype_mind_scan_robofac ) ); + item *scan = item::spawn( itype_mind_scan_robofac ); + data_card.put_in( *scan ); } diff --git a/src/activity_handlers.h b/src/activity_handlers.h index d7be822d5f15..2f47f55c147b 100644 --- a/src/activity_handlers.h +++ b/src/activity_handlers.h @@ -11,6 +11,7 @@ #include "optional.h" #include "type_id.h" +#include "colony.h" class Character; class inventory; @@ -133,10 +134,10 @@ enum class item_drop_reason { tumbling }; -void put_into_vehicle_or_drop( Character &c, item_drop_reason, const std::list &items ); -void put_into_vehicle_or_drop( Character &c, item_drop_reason, const std::list &items, +void put_into_vehicle_or_drop( Character &c, item_drop_reason, const ItemList &items ); +void put_into_vehicle_or_drop( Character &c, item_drop_reason, const ItemList &items, const tripoint &where, bool force_ground = false ); -void drop_on_map( Character &c, item_drop_reason reason, const std::list &items, +void drop_on_map( Character &c, item_drop_reason reason, const ItemList &items, const tripoint &where ); namespace activity_handlers diff --git a/src/activity_item_handling.cpp b/src/activity_item_handling.cpp index b22e8d9d3bf5..9abe5d5677da 100644 --- a/src/activity_item_handling.cpp +++ b/src/activity_item_handling.cpp @@ -152,14 +152,14 @@ void cancel_aim_processing(); //Generic activity: maximum search distance for zones, constructions, etc. const int ACTIVITY_SEARCH_DISTANCE = 60; -static bool same_type( const std::list &items ) +static bool same_type( const ItemList &items ) { - return std::all_of( items.begin(), items.end(), [&items]( const item & it ) { - return it.type == items.begin()->type; + return std::all_of( items.begin(), items.end(), [&items]( const item * const & it ) { + return it->type == ( *items.begin() )->type; } ); } -static void put_into_vehicle( Character &c, item_drop_reason reason, const std::list &items, +static void put_into_vehicle( Character &c, item_drop_reason reason, const ItemList &items, vehicle &veh, int part ) { if( items.empty() ) { @@ -174,30 +174,30 @@ static void put_into_vehicle( Character &c, item_drop_reason reason, const std:: // can't use constant reference here because of the spill_contents() for( auto it : items ) { - if( pickup::handle_spillable_contents( c, it, here ) ) { + if( pickup::handle_spillable_contents( c, *it, here ) ) { continue; } - if( veh.add_item( part, it ) ) { - into_vehicle_count += it.count(); + if( veh.add_item( part, *it ) ) { + into_vehicle_count += it->count(); } else { - if( it.count_by_charges() ) { + if( it->count_by_charges() ) { // Maybe we can add a few charges in the trunk and the rest on the ground. - auto charges_added = veh.add_charges( part, it ); - it.mod_charges( -charges_added ); + auto charges_added = veh.add_charges( part, *it ); + it->mod_charges( -charges_added ); into_vehicle_count += charges_added; } - here.add_item_or_charges( where, it ); - fallen_count += it.count(); + here.add_item_or_charges( where, *it ); + fallen_count += it->count(); } - it.handle_pickup_ownership( c ); + it->handle_pickup_ownership( c ); } const std::string part_name = veh.part_info( part ).name(); if( same_type( items ) ) { - const item &it = items.front(); - const int dropcount = items.size() * it.count(); - const std::string it_name = it.tname( dropcount ); + const item *const &it = items.front(); + const int dropcount = items.size() * it->count(); + const std::string it_name = it->tname( dropcount ); switch( reason ) { case item_drop_reason::deliberate: @@ -273,37 +273,38 @@ static void put_into_vehicle( Character &c, item_drop_reason reason, const std:: } } -static void pass_to_ownership_handling( item obj, Character &c ) +static void pass_to_ownership_handling( item &obj, Character &c ) { obj.handle_pickup_ownership( c ); } -static void stash_on_pet( const std::list &items, monster &pet, Character &who ) +static void stash_on_pet( const ItemList &items, monster &pet, Character &who ) { - units::volume remaining_volume = pet.storage_item->get_storage() - pet.get_carried_volume(); + //TODO!: null check? + units::volume remaining_volume = pet.get_storage_item()->get_storage() - pet.get_carried_volume(); units::mass remaining_weight = pet.weight_capacity() - pet.get_carried_weight(); map &here = get_map(); - for( const item &it : items ) { - if( it.volume() > remaining_volume ) { - add_msg( m_bad, _( "%1$s did not fit and fell to the %2$s." ), it.display_name(), + for( item * const &it : items ) { + if( it->volume() > remaining_volume ) { + add_msg( m_bad, _( "%1$s did not fit and fell to the %2$s." ), it->display_name(), here.name( pet.pos() ) ); - here.add_item_or_charges( pet.pos(), it ); - } else if( it.weight() > remaining_weight ) { - add_msg( m_bad, _( "%1$s is too heavy and fell to the %2$s." ), it.display_name(), + here.add_item_or_charges( pet.pos(), *it ); + } else if( it->weight() > remaining_weight ) { + add_msg( m_bad, _( "%1$s is too heavy and fell to the %2$s." ), it->display_name(), here.name( pet.pos() ) ); - here.add_item_or_charges( pet.pos(), it ); + here.add_item_or_charges( pet.pos(), *it ); } else { - pet.add_item( it ); - remaining_volume -= it.volume(); - remaining_weight -= it.weight(); + pet.add_item( *it ); + remaining_volume -= it->volume(); + remaining_weight -= it->weight(); } // TODO: if NPCs can have pets or move items onto pets - pass_to_ownership_handling( it, who ); + pass_to_ownership_handling( *it, who ); } } -void drop_on_map( Character &c, item_drop_reason reason, const std::list &items, +void drop_on_map( Character &c, item_drop_reason reason, const std::vector &items, const tripoint &where ) { if( items.empty() ) { @@ -314,9 +315,9 @@ void drop_on_map( Character &c, item_drop_reason reason, const std::list & const bool can_move_there = here.passable( where ); if( same_type( items ) ) { - const item &it = items.front(); - const int dropcount = items.size() * it.count(); - const std::string it_name = it.tname( dropcount ); + const item *const &it = items.front(); + const int dropcount = items.size() * it->count(); + const std::string it_name = it->tname( dropcount ); switch( reason ) { case item_drop_reason::deliberate: @@ -386,17 +387,19 @@ void drop_on_map( Character &c, item_drop_reason reason, const std::list & } } for( auto &it : items ) { - here.add_item_or_charges( where, it ); - pass_to_ownership_handling( it, c ); + here.add_item_or_charges( where, *it ); + pass_to_ownership_handling( *it, c ); } } -void put_into_vehicle_or_drop( Character &c, item_drop_reason reason, const std::list &items ) +void put_into_vehicle_or_drop( Character &c, item_drop_reason reason, + const std::vector &items ) { return put_into_vehicle_or_drop( c, reason, items, c.pos() ); } -void put_into_vehicle_or_drop( Character &c, item_drop_reason reason, const std::list &items, +void put_into_vehicle_or_drop( Character &c, item_drop_reason reason, + const std::vector &items, const tripoint &where, bool force_ground ) { map &here = get_map(); @@ -410,15 +413,15 @@ void put_into_vehicle_or_drop( Character &c, item_drop_reason reason, const std: static std::list convert_to_items( Character &p, const drop_locations &drop, - std::function filter ) + std::function filter ) { std::list res; for( const drop_location &rec : drop ) { - const item_location loc = rec.loc; + item *loc = &*rec.loc; const int count = rec.count; - if( !filter( loc ) ) { + if( !filter( *loc ) ) { continue; } else if( !p.is_worn( *loc ) && !p.is_wielding( *loc ) ) { // Special case. After dropping the first few items, the remaining items are already separated. @@ -428,21 +431,20 @@ static std::list convert_to_items( Character &p, // but each time stopping after visiting the first item. // In the end, we would add references to the same item (the first one in the stack) multiple times. if( count == 1 ) { - res.emplace_back( loc, 1, loc.obtain_cost( p, 1 ) ); + res.emplace_back( *loc, 1, loc->obtain_cost( p, 1 ) ); continue; } int obtained = 0; - for( const item &it : p.inv.const_stack( p.get_item_position( &*loc ) ) ) { + for( item * const &it : p.inv.const_stack( p.get_item_position( &*loc ) ) ) { if( obtained >= count ) { break; } - const int qty = it.count_by_charges() ? std::min( it.charges, count - obtained ) : 1; + const int qty = it->count_by_charges() ? std::min( it->charges, count - obtained ) : 1; obtained += qty; - item_location loc( p, const_cast( &it ) ); - res.emplace_back( loc, qty, loc.obtain_cost( p, qty ) ); + res.emplace_back( *it, qty, it->obtain_cost( p, qty ) ); } } else { - res.emplace_back( loc, count, p.is_wielding( *loc ) ? 0 : loc.obtain_cost( p ) ); + res.emplace_back( *loc, count, p.is_wielding( *loc ) ? 0 : loc->obtain_cost( p ) ); } } @@ -458,16 +460,16 @@ namespace pickup std::list reorder_for_dropping( Character &p, const drop_locations &drop ) { std::list res = convert_to_items( p, drop, - [&p]( item_location loc ) { - return p.is_wielding( *loc ); + [&p]( item & loc ) { + return p.is_wielding( loc ); } ); std::list inv = convert_to_items( p, drop, - [&p]( item_location loc ) { - return !p.is_wielding( *loc ) && !p.is_worn( *loc ); + [&p]( item & loc ) { + return !p.is_wielding( loc ) && !p.is_worn( loc ); } ); std::list worn = convert_to_items( p, drop, - [&p]( item_location loc ) { - return p.is_worn( *loc ); + [&p]( item & loc ) { + return p.is_worn( loc ); } ); // Sort inventory items by volume in ascending order @@ -484,9 +486,8 @@ std::list reorder_for_dropping( Character &p, const drop_locations &dr if( iter == worn.end() ) { // TODO: Use a calculated cost - const item_location loc( p, dit ); - act_item act( loc, loc->count(), loc.obtain_cost( p, loc->count() ) ); - worn.emplace_front( loc, loc->count(), loc.obtain_cost( p ) ); + act_item act( *dit, dit->count(), dit->obtain_cost( p, dit->count() ) ); + worn.emplace_front( *dit, dit->count(), dit->obtain_cost( p ) ); } } } @@ -521,10 +522,10 @@ std::list reorder_for_dropping( Character &p, const drop_locations &dr if( inv_indices.count( i ) != 0 ) { continue; } - std::list &inv_stack = *old_inv[i]; - for( item &item : inv_stack ) { + ItemList &inv_stack = *old_inv[i]; + for( item *&item : inv_stack ) { // Note: zero cost, but won't be contained on drop - act_item to_drop = act_item( item_location( p, &item ), item.count(), 0 ); + act_item to_drop = act_item( *item, item->count(), 0 ); inv.push_back( to_drop ); excessive_volume -= to_drop.loc->volume(); if( excessive_volume <= 0_ml ) { @@ -574,37 +575,19 @@ std::list reorder_for_dropping( Character &p, const drop_locations &dr return res; } -static bool try_rebuild_if_needed( item_location &loc ) +//TODO!: check pointers, needs checking for loaded after making safe as well +ItemList obtain_and_tokenize_items( player &p, std::list &items ) { - if( loc ) { - return true; - } - loc.make_dirty(); - if( !loc ) { - debugmsg( "Lost target item of ACT_DROP and couldn't rebuild item_location!" ); - } - - return static_cast( loc ); -} - -std::list obtain_and_tokenize_items( player &p, std::list &items ) -{ - std::list res; + ItemList res; drop_token_provider &token_provider = drop_token::get_provider(); item_drop_token last_token = token_provider.make_next( calendar::turn ); - if( !try_rebuild_if_needed( items.front().loc ) ) { + if( items.empty() ) { return res; } units::volume last_storage_volume = items.front().loc->get_storage(); while( !items.empty() && ( p.is_npc() || p.moves > 0 || items.front().consumed_moves == 0 ) ) { act_item &ait = items.front(); - if( !try_rebuild_if_needed( ait.loc ) ) { - debugmsg( "Lost target item of ACT_DROP" ); - items.pop_front(); - return res; - } - p.mod_moves( -ait.consumed_moves ); if( p.is_worn( *ait.loc ) ) { @@ -617,11 +600,11 @@ std::list obtain_and_tokenize_items( player &p, std::list &items } else if( ait.loc->count_by_charges() ) { res.push_back( p.reduce_charges( const_cast( &*ait.loc ), ait.count ) ); } else { - res.push_back( p.i_rem( &*ait.loc ) ); + res.push_back( &p.i_rem( &*ait.loc ) ); } // TODO: Get the item consistently instead of using back() - item ¤t_drop = res.back(); + item ¤t_drop = *res.back(); // Hack: if it consumes zero moves, it must have been contained // TODO: Properly mark containment somehow @@ -657,7 +640,7 @@ static void debug_drop_list( const std::list &list ) popup( res, PF_GET_KEY ); } -static void debug_tokens( const std::list &items ) +static void debug_tokens( const ItemList &items ) { if( !debug_mode ) { return; @@ -665,17 +648,18 @@ static void debug_tokens( const std::list &items ) std::stringstream ss; ss << "Item tokens:\n"; - for( const item &it : items ) { - ss << it.display_name() << ": " << *it.drop_token << '\n'; + for( const item * const &it : items ) { + ss << it->display_name() << ": " << *it->drop_token << '\n'; } popup( ss.str(), PF_GET_KEY ); } -static std::list obtain_activity_items( Character &who, std::list &targets ) +static ItemList obtain_activity_items( Character &who, + std::list &targets ) { debug_drop_list( targets ); - std::list res = pickup::obtain_and_tokenize_items( *who.as_player(), targets ); + ItemList res = pickup::obtain_and_tokenize_items( *who.as_player(), targets ); debug_tokens( res ); @@ -699,7 +683,7 @@ void activity_on_turn_wear( player_activity &act, player &p ) { // ACT_WEAR has item_location targets, and int quantities while( p.moves > 0 && !act.targets.empty() ) { - item_location target = std::move( act.targets.back() ); + safe_reference target = std::move( act.targets.back() ); int quantity = act.values.back(); act.targets.pop_back(); act.values.pop_back(); @@ -708,10 +692,10 @@ void activity_on_turn_wear( player_activity &act, player &p ) debugmsg( "Lost target item of ACT_WEAR" ); continue; } - + //TODO!: fuck that hard cheeeck ON THIS WHOLE SHIT // Make copies so the original remains untouched if wearing fails - item newit = *target; - item leftovers = newit; + item &newit = *target; + item &leftovers = newit;//Not sure about this might need a real copy // Handle charges, quantity == 0 means move all if( quantity != 0 && newit.count_by_charges() ) { @@ -727,9 +711,11 @@ void activity_on_turn_wear( player_activity &act, player &p ) // If we wore up a whole stack, remove the original item // Otherwise, replace the item with the leftovers if( leftovers.charges > 0 ) { - *target = std::move( leftovers ); + //TODO!: restore + // target = leftovers; } else { - target.remove_item(); + target->detach(); + target->destroy(); } } } @@ -740,6 +726,7 @@ void activity_on_turn_wear( player_activity &act, player &p ) } } +//TODO!: update for item arenas void wash_activity_actor::finish( player_activity &act, Character &who ) { // Check again that we have enough water and soap incase the amount in our inventory changed somehow @@ -777,12 +764,13 @@ void wash_activity_actor::finish( player_activity &act, Character &who ) } it.item_tags.erase( "FILTHY" ); } else { - item it2 = it; + //TODO!: checkkkk + item &it2 = *item::spawn( it ); it.charges -= i.count; it2.charges = i.count; it2.item_tags.erase( "FILTHY" ); - std::list tmp; - tmp.push_back( it2 ); + ItemList tmp; + tmp.push_back( &it2 ); put_into_vehicle_or_drop( who, item_drop_reason::deliberate, tmp ); } who.on_worn_item_washed( it ); @@ -968,8 +956,7 @@ static bool vehicle_activity( player &p, const tripoint &src_loc, int vpindex, c // values[7] p.activity.values.push_back( here.getabs( src_loc ).z ); // this would only be used for refilling tasks - item_location target; - p.activity.targets.emplace_back( std::move( target ) ); + p.activity.targets.emplace_back( safe_reference() ); p.activity.placement = here.getabs( src_loc ); p.activity_vehicle_part_index = -1; return true; @@ -979,7 +966,8 @@ static void move_item( player &p, item &it, const int quantity, const tripoint & const tripoint &dest, vehicle *src_veh, int src_part, const activity_id &activity_to_restore = activity_id::NULL_ID() ) { - item leftovers = it; + //TODO!: more splitting checks etc + item &leftovers = *item::spawn( it ); if( quantity != 0 && it.count_by_charges() ) { // Reinserting leftovers happens after item removal to avoid stacking issues. @@ -1000,7 +988,7 @@ static void move_item( player &p, item &it, const int quantity, const tripoint & } else if( activity_to_restore == ACT_FETCH_REQUIRED ) { it.set_var( "activity_var", p.name ); } - put_into_vehicle_or_drop( p, item_drop_reason::deliberate, { it }, dest ); + put_into_vehicle_or_drop( p, item_drop_reason::deliberate, { &it }, dest ); // Remove from map or vehicle. if( src_veh ) { src_veh->remove_item( src_part, &it ); @@ -1230,17 +1218,17 @@ static bool are_requirements_nearby( const std::vector &loot_spots, } } for( const auto &elem2 : here.i_at( elem ) ) { - if( in_loot_zones && elem2.made_of( LIQUID ) ) { + if( in_loot_zones && elem2->made_of( LIQUID ) ) { continue; } if( check_weight ) { // this fetch task will need to pick up an item. so check for its weight/volume before setting off. - if( in_loot_zones && ( elem2.volume() > volume_allowed || - elem2.weight() > weight_allowed ) ) { + if( in_loot_zones && ( elem2->volume() > volume_allowed || + elem2->weight() > weight_allowed ) ) { continue; } } - temp_inv += elem2; + temp_inv += *elem2; } if( !in_loot_zones ) { if( const cata::optional vp = here.veh_at( elem ).part_with_feature( "CARGO", @@ -1248,7 +1236,7 @@ static bool are_requirements_nearby( const std::vector &loot_spots, vehicle &src_veh = vp->vehicle(); int src_part = vp->part_index(); for( auto &it : src_veh.get_items( src_part ) ) { - temp_inv += it; + temp_inv += *it; } } } @@ -1261,11 +1249,11 @@ static bool are_requirements_nearby( const std::vector &loot_spots, vehicle &veh = vp->vehicle(); const cata::optional weldpart = vp.part_with_feature( "WELDRIG", true ); if( weldpart ) { - item welder( itype_welder, calendar::start_of_cataclysm ); + item &welder = *item::spawn( itype_welder, calendar::start_of_cataclysm ); welder.charges = veh.fuel_left( itype_battery, true ); welder.set_flag( "PSEUDO" ); temp_inv.add_item( welder ); - item soldering_iron( itype_soldering_iron, calendar::start_of_cataclysm ); + item &soldering_iron = *item::spawn( itype_soldering_iron, calendar::start_of_cataclysm ); soldering_iron.charges = veh.fuel_left( itype_battery, true ); soldering_iron.set_flag( "PSEUDO" ); temp_inv.add_item( soldering_iron ); @@ -1351,7 +1339,7 @@ static activity_reason_info can_do_activity_there( const activity_id &act, playe if( !has_skill_for_vehicle_work( vpinfo.removal_skills, p ) ) { continue; } - item base( vpinfo.item ); + item &base = *item::spawn_temporary( vpinfo.item ); if( base.is_wheel() ) { // no wheel removal yet continue; @@ -1452,13 +1440,13 @@ static activity_reason_info can_do_activity_there( const activity_id &act, playe } } if( act == ACT_MULTIPLE_BUTCHER ) { - std::vector corpses; + std::vector corpses; int big_count = 0; int small_count = 0; for( const auto &i : here.i_at( src_loc ) ) { // make sure nobody else is working on that corpse right now - if( i.is_corpse() && !i.has_var( "activity_var" ) ) { - const mtype corpse = *i.get_mtype(); + if( i->is_corpse() && !i->has_var( "activity_var" ) ) { + const mtype corpse = *i->get_mtype(); if( corpse.size >= MS_MEDIUM ) { big_count += 1; } else { @@ -1505,7 +1493,7 @@ static activity_reason_info can_do_activity_there( const activity_id &act, playe if( act == ACT_MULTIPLE_CHOP_PLANKS ) { //are there even any logs there? for( auto &i : here.i_at( src_loc ) ) { - if( i.typeId() == itype_log ) { + if( i->typeId() == itype_log ) { // do we have an axe? if( p.has_quality( qual_AXE, 1 ) ) { return activity_reason_info::ok( do_activity_reason::NEEDS_CHOPPING ); @@ -1693,38 +1681,38 @@ static std::vector> requirements_map( player // will be filtered for amounts/charges afterwards. for( const tripoint &point_elem : pickup_task ? loot_spots : combined_spots ) { std::map temp_map; - for( const item &stack_elem : here.i_at( point_elem ) ) { + for( const item * const &stack_elem : here.i_at( point_elem ) ) { for( std::vector &elem : req_comps ) { for( item_comp &comp_elem : elem ) { - if( comp_elem.type == stack_elem.typeId() ) { + if( comp_elem.type == stack_elem->typeId() ) { // if its near the work site, we can remove a count from the requirements. // if two "lines" of the requirement have the same component appearing again // that is fine, we will choose which "line" to fulfill later, and the decrement will count towards that then. if( !pickup_task && std::find( already_there_spots.begin(), already_there_spots.end(), point_elem ) != already_there_spots.end() ) { - comp_elem.count -= stack_elem.count(); + comp_elem.count -= stack_elem->count(); } - temp_map[stack_elem.typeId()] += stack_elem.count(); + temp_map[stack_elem->typeId()] += stack_elem->count(); } } } for( std::vector &elem : tool_comps ) { for( tool_comp &comp_elem : elem ) { - if( comp_elem.type == stack_elem.typeId() ) { + if( comp_elem.type == stack_elem->typeId() ) { if( !pickup_task && std::find( already_there_spots.begin(), already_there_spots.end(), point_elem ) != already_there_spots.end() ) { - comp_elem.count -= stack_elem.count(); + comp_elem.count -= stack_elem->count(); } if( comp_elem.by_charges() ) { // we don't care if there are 10 welders with 5 charges each // we only want the one welder that has the required charge. - if( stack_elem.ammo_remaining() >= comp_elem.count ) { - temp_map[stack_elem.typeId()] += stack_elem.ammo_remaining(); + if( stack_elem->ammo_remaining() >= comp_elem.count ) { + temp_map[stack_elem->typeId()] += stack_elem->ammo_remaining(); } } else { - temp_map[stack_elem.typeId()] += stack_elem.count(); + temp_map[stack_elem->typeId()] += stack_elem->count(); } } } @@ -1734,13 +1722,13 @@ static std::vector> requirements_map( player for( quality_requirement &comp_elem : elem ) { const quality_id tool_qual = comp_elem.type; const int qual_level = comp_elem.level; - if( stack_elem.has_quality( tool_qual, qual_level ) ) { + if( stack_elem->has_quality( tool_qual, qual_level ) ) { if( !pickup_task && std::find( already_there_spots.begin(), already_there_spots.end(), point_elem ) != already_there_spots.end() ) { - comp_elem.count -= stack_elem.count(); + comp_elem.count -= stack_elem->count(); } - temp_map[stack_elem.typeId()] += stack_elem.count(); + temp_map[stack_elem->typeId()] += stack_elem->count(); } } } @@ -1888,7 +1876,8 @@ static std::vector> requirements_map( player for( auto it = requirement_map.begin(); it != requirement_map.end(); ) { tripoint pos_here = std::get<0>( *it ); itype_id item_here = std::get<1>( *it ); - item test_item = item( item_here, calendar::start_of_cataclysm ); + //TODO!: Check avoiding this construction, it's a bad one + item &test_item = *item::spawn_temporary( item_here, calendar::start_of_cataclysm ); if( test_item.has_quality( tool_qual, qual_level ) ) { // it's just this spot that can fulfil the requirement on its own final_map.push_back( std::make_tuple( pos_here, item_here, 1 ) ); @@ -1916,7 +1905,7 @@ static bool construction_activity( player &p, const zone_data * /*zone*/, const return false; } const construction &built_chosen = act_info.con_idx->obj(); - std::list used; + ItemList used; // create the partial construction struct partial_con pc; pc.id = built_chosen.id; @@ -1928,8 +1917,8 @@ static bool construction_activity( player &p, const zone_data * /*zone*/, const } // Use up the components for( const std::vector &it : built_chosen.requirements->get_components() ) { - std::list tmp = p.consume_items( it, 1, is_crafting_component ); - used.splice( used.end(), tmp ); + ItemList tmp = p.consume_items( it, 1, is_crafting_component ); + used.insert( used.end(), tmp.begin(), tmp.end() ); } pc.components = used; here.partial_con_set( src_loc, pc ); @@ -1977,8 +1966,8 @@ static bool tidy_activity( player &p, const tripoint &src_loc, dest_part = -1; } for( auto &it : items_there ) { - if( it.has_var( "activity_var" ) && it.get_var( "activity_var", "" ) == p.name ) { - move_item( p, it, it.count(), src_loc, loot_src_lot, dest_veh, dest_part, + if( it->has_var( "activity_var" ) && it->get_var( "activity_var", "" ) == p.name ) { + move_item( p, *it, it->count(), src_loc, loot_src_lot, dest_veh, dest_part, activity_to_restore ); break; } @@ -1988,7 +1977,7 @@ static bool tidy_activity( player &p, const tripoint &src_loc, for( item *inv_elem : p.inv_dump() ) { if( inv_elem->has_var( "activity_var" ) ) { inv_elem->erase_var( "activity_var" ); - put_into_vehicle_or_drop( p, item_drop_reason::deliberate, { *inv_elem }, src_loc ); + put_into_vehicle_or_drop( p, item_drop_reason::deliberate, { inv_elem }, src_loc ); p.i_rem( inv_elem ); } } @@ -2020,9 +2009,9 @@ static bool fetch_activity( player &p, const tripoint &src_loc, if( src_veh ) { for( auto &veh_elem : src_veh->get_items( src_part ) ) { for( auto elem : mental_map_2 ) { - if( std::get<0>( elem ) == src_loc && veh_elem.typeId() == std::get<1>( elem ) ) { + if( std::get<0>( elem ) == src_loc && veh_elem->typeId() == std::get<1>( elem ) ) { if( !p.backlog.empty() && p.backlog.front().id() == ACT_MULTIPLE_CONSTRUCTION ) { - move_item( p, veh_elem, veh_elem.count_by_charges() ? std::get<2>( elem ) : 1, src_loc, + move_item( p, *veh_elem, veh_elem->count_by_charges() ? std::get<2>( elem ) : 1, src_loc, here.getlocal( p.backlog.front().coords.back() ), src_veh, src_part, activity_to_restore ); return true; } @@ -2031,7 +2020,7 @@ static bool fetch_activity( player &p, const tripoint &src_loc, } } for( auto item_iter = items_there.begin(); item_iter != items_there.end(); item_iter++ ) { - item &it = *item_iter; + item &it = **item_iter; for( auto elem : mental_map_2 ) { if( std::get<0>( elem ) == src_loc && it.typeId() == std::get<1>( elem ) ) { // construction/crafting tasks want the required item moved near the work spot. @@ -2051,7 +2040,8 @@ static bool fetch_activity( player &p, const tripoint &src_loc, if( it.volume() > volume_allowed || it.weight() > weight_allowed ) { continue; } - item leftovers = it; + //TODO!: check split + item &leftovers = *item::spawn( it ); if( pickup_count != 1 && it.count_by_charges() ) { // Reinserting leftovers happens after item removal to avoid stacking issues. leftovers.charges = it.charges - pickup_count; @@ -2096,21 +2086,21 @@ static bool butcher_corpse_activity( player &p, const tripoint &src_loc, map &here = get_map(); map_stack items = here.i_at( src_loc ); for( auto &elem : items ) { - if( elem.is_corpse() && !elem.has_var( "activity_var" ) ) { - const mtype corpse = *elem.get_mtype(); + if( elem->is_corpse() && !elem->has_var( "activity_var" ) ) { + const mtype corpse = *elem->get_mtype(); if( corpse.size >= MS_MEDIUM && reason != do_activity_reason::NEEDS_BIG_BUTCHERING ) { continue; } - elem.set_var( "activity_var", p.name ); + elem->set_var( "activity_var", p.name ); p.assign_activity( ACT_BUTCHER_FULL, 0, true ); - p.activity.targets.emplace_back( map_cursor( src_loc ), &elem ); + p.activity.targets.emplace_back( elem ); p.activity.placement = here.getabs( src_loc ); return true; } } return false; } - +//TODO!: check this i_rem static bool chop_plank_activity( player &p, const tripoint &src_loc ) { item *best_qual = p.best_quality_item( qual_AXE ); @@ -2122,8 +2112,8 @@ static bool chop_plank_activity( player &p, const tripoint &src_loc ) } map &here = get_map(); for( auto &i : here.i_at( src_loc ) ) { - if( i.typeId() == itype_log ) { - here.i_rem( src_loc, &i ); + if( i->typeId() == itype_log ) { + here.i_rem( src_loc, i ); int moves = to_moves( 20_minutes ); p.add_msg_if_player( _( "You cut the log into planks." ) ); p.assign_activity( ACT_CHOP_PLANKS, moves, -1 ); @@ -2287,22 +2277,22 @@ void activity_on_turn_move_loot( player_activity &act, player &p ) src_veh = &vp->vehicle(); src_part = vp->part_index(); for( auto &it : src_veh->get_items( src_part ) ) { - if( !it.is_owned_by( p, true ) ) { + if( !it->is_owned_by( p, true ) ) { continue; } - it.set_owner( p ); - items.push_back( std::make_pair( &it, true ) ); + it->set_owner( p ); + items.push_back( std::make_pair( it, true ) ); } } else { src_veh = nullptr; src_part = -1; } for( auto &it : here.i_at( src_loc ) ) { - if( !it.is_owned_by( p, true ) ) { + if( !it->is_owned_by( p, true ) ) { continue; } - it.set_owner( p ); - items.push_back( std::make_pair( &it, false ) ); + it->set_owner( p ); + items.push_back( std::make_pair( it, false ) ); } //Skip items that have already been processed @@ -2433,7 +2423,7 @@ static bool mine_activity( player &p, const tripoint &src_loc ) moves /= 2; } p.assign_activity( powered ? ACT_JACKHAMMER : ACT_PICKAXE, moves ); - p.activity.targets.push_back( item_location( p, chosen_item ) ); + p.activity.targets.push_back( chosen_item ); p.activity.placement = here.getabs( src_loc ); return true; @@ -2550,8 +2540,8 @@ static std::unordered_set generic_multi_activity_locations( player &p, if( found_one_point ) { break; } - for( const item &stack_elem : here.i_at( elem ) ) { - if( stack_elem.has_var( "activity_var" ) && stack_elem.get_var( "activity_var", "" ) == p.name ) { + for( const item * const &stack_elem : here.i_at( elem ) ) { + if( stack_elem->has_var( "activity_var" ) && stack_elem->get_var( "activity_var", "" ) == p.name ) { const furn_t &f = here.furn( elem ).obj(); if( !f.has_flag( flag_PLANT ) ) { src_set.insert( here.getabs( elem ) ); @@ -2922,7 +2912,7 @@ static bool generic_multi_activity_do( player &p, const activity_id &act_id, item *best_rod = p.best_quality_item( qual_FISHING ); p.assign_activity( ACT_FISH, to_moves( 5_hours ), 0, 0, best_rod->tname() ); - p.activity.targets.push_back( item_location( p, best_rod ) ); + p.activity.targets.push_back( best_rod ); p.activity.coord_set = g->get_fishable_locations( ACTIVITY_SEARCH_DISTANCE, src_loc ); return false; } else if( reason == do_activity_reason::NEEDS_MINING ) { @@ -3173,18 +3163,18 @@ bool find_auto_consume( player &p, const bool food ) int index = veh.part_with_feature( vp->part_index(), "CARGO", false ); if( index >= 0 ) { vehicle_stack vehitems = veh.get_items( index ); - for( item &it : vehitems ) { - items_here.push_back( &it ); + for( item *&it : vehitems ) { + items_here.push_back( it ); } } } else { map_stack mapitems = here.i_at( here.getlocal( loc ) ); - for( item &it : mapitems ) { - items_here.push_back( &it ); + for( item *&it : mapitems ) { + items_here.push_back( it ); } } - for( item *it : items_here ) { + for( item *&it : items_here ) { item &comest = p.get_consumable_from( *it ); if( comest.is_null() || comest.is_craft() || !comest.is_food() || p.fun_for( comest ).first < -5 ) { @@ -3221,17 +3211,7 @@ bool find_auto_consume( player &p, const bool food ) p.mod_moves( -pickup::cost_to_move_item( p, *it ) * std::max( rl_dist( p.pos(), here.getlocal( loc ) ), 1 ) ); - item_location item_loc; - if( vp ) { - item_loc = item_location( vehicle_cursor( vp->vehicle(), vp->part_index() ), &comest ); - } else { - item_loc = item_location( map_cursor( here.getlocal( loc ) ), &comest ); - } - avatar_action::eat( g->u, item_loc ); - // eat() may have removed the item, so check its still there. - if( item_loc.get_item() && item_loc->is_container() ) { - item_loc->on_contents_changed(); - } + avatar_action::eat( g->u, &comest ); return true; } } @@ -3268,14 +3248,14 @@ void try_fuel_fire( player_activity &act, player &p, const bool starting_fire ) // Maybe TODO: - refueling in the rain could use more fuel // First, simulate expected burn per turn, to see if we need more fuel map_stack fuel_on_fire = here.i_at( *best_fire ); - for( item &it : fuel_on_fire ) { - it.simulate_burn( fd ); + for( item *&it : fuel_on_fire ) { + it->simulate_burn( fd ); // Unconstrained fires grow below -50_minutes age - if( !contained && fire_age < -40_minutes && fd.fuel_produced > 1.0f && !it.made_of( LIQUID ) ) { + if( !contained && fire_age < -40_minutes && fd.fuel_produced > 1.0f && !it->made_of( LIQUID ) ) { // Too much - we don't want a firestorm! // Move item back to refueling pile // Note: move_item() handles messages (they're the generic "you drop x") - move_item( p, it, 0, *best_fire, *refuel_spot, nullptr, -1 ); + move_item( p, *it, 0, *best_fire, *refuel_spot, nullptr, -1 ); return; } } @@ -3288,17 +3268,17 @@ void try_fuel_fire( player_activity &act, player &p, const bool starting_fire ) // We need to move fuel from stash to fire map_stack potential_fuel = here.i_at( *refuel_spot ); - for( item &it : potential_fuel ) { - if( it.made_of( LIQUID ) ) { + for( item *&it : potential_fuel ) { + if( it->made_of( LIQUID ) ) { continue; } float last_fuel = fd.fuel_produced; - it.simulate_burn( fd ); + it->simulate_burn( fd ); if( fd.fuel_produced > last_fuel ) { - int quantity = std::max( 1, std::min( it.charges, it.charges_per_volume( 250_ml ) ) ); + int quantity = std::max( 1, std::min( it->charges, it->charges_per_volume( 250_ml ) ) ); // Note: move_item() handles messages (they're the generic "you drop x") - move_item( p, it, quantity, *refuel_spot, *best_fire, nullptr, -1 ); + move_item( p, *it, quantity, *refuel_spot, *best_fire, nullptr, -1 ); return; } } diff --git a/src/advanced_inv.cpp b/src/advanced_inv.cpp index 255ef5924140..8da005795efb 100644 --- a/src/advanced_inv.cpp +++ b/src/advanced_inv.cpp @@ -32,7 +32,6 @@ #include "item.h" #include "item_category.h" #include "item_contents.h" -#include "item_location.h" #include "item_stack.h" #include "map.h" #include "map_selector.h" @@ -334,7 +333,6 @@ void advanced_inventory::print_items( const advanced_inventory_pane &pane, bool } if( it.is_money() ) { //Count charges - // TODO: transition to the item_location system used for the normal inventory unsigned int charges_total = 0; for( const auto item : sitem.items ) { charges_total += item->charges; @@ -847,21 +845,20 @@ bool advanced_inventory::move_all_items( bool nested_call ) if( spane.get_area() == AIM_INVENTORY ) { for( size_t index = 0; index < g->u.inv.size(); ++index ) { - const std::list &stack = g->u.inv.const_stack( index ); - const item &it = stack.front(); - item_location indexed_item( g->u, const_cast( &it ) ); + const ItemList &stack = g->u.inv.const_stack( index ); + item *const &it = stack.front(); - if( !spane.is_filtered( it ) ) { + if( !spane.is_filtered( *it ) ) { int count; - if( it.count_by_charges() ) { - count = it.charges; + if( it->count_by_charges() ) { + count = it->charges; } else { count = stack.size(); } - if( it.is_favorite ) { - dropped_favorite.emplace_back( indexed_item, count ); + if( it->is_favorite ) { + dropped_favorite.emplace_back( *it, count ); } else { - dropped.emplace_back( indexed_item, count ); + dropped.emplace_back( *it, count ); } } } @@ -869,18 +866,17 @@ bool advanced_inventory::move_all_items( bool nested_call ) // do this in reverse, to account for vector item removal messing with future indices auto iter = g->u.worn.rbegin(); for( size_t idx = 0; idx < g->u.worn.size(); ++idx, ++iter ) { - item &it = *iter; + item &it = **iter; if( !g->u.can_takeoff( it ).success() ) { continue; } if( !spane.is_filtered( it ) ) { - item_location loc( g->u, &it ); if( it.is_favorite ) { - dropped_favorite.emplace_back( loc, it.count() ); + dropped_favorite.emplace_back( it, it.count() ); } else { - dropped.emplace_back( loc, it.count() ); + dropped.emplace_back( it, it.count() ); } } } @@ -909,7 +905,7 @@ bool advanced_inventory::move_all_items( bool nested_call ) const tripoint relative_destination = darea.off; // Find target items and quantities thereof for the new activity - std::vector target_items; + std::vector target_items; std::vector quantities; item_stack::iterator stack_begin, stack_end; @@ -929,17 +925,17 @@ bool advanced_inventory::move_all_items( bool nested_call ) bool filtered_any_bucket = false; // Push item_locations and item counts for all items at placement for( item_stack::iterator it = stack_begin; it != stack_end; ++it ) { - if( spane.is_filtered( *it ) ) { + if( spane.is_filtered( **it ) ) { continue; } - if( filter_buckets && it->is_bucket_nonempty() ) { + if( filter_buckets && ( *it )->is_bucket_nonempty() ) { filtered_any_bucket = true; continue; } if( spane.in_vehicle() ) { - target_items.emplace_back( vehicle_cursor( *sarea.veh, sarea.vstor ), &*it ); + target_items.emplace_back( *it ); } else { - target_items.emplace_back( map_cursor( sarea.pos ), &*it ); + target_items.emplace_back( *it ); } // quantity of 0 means move all quantities.push_back( 0 ); @@ -1122,7 +1118,7 @@ void advanced_inventory::change_square( const aim_location changeSquare, } } -void advanced_inventory::start_activity( const aim_location destarea, const aim_location srcarea, +void advanced_inventory::start_activity( const aim_location destarea, advanced_inv_listitem *sitem, int &amount_to_move, const bool from_vehicle, const bool to_vehicle ) const { @@ -1133,47 +1129,29 @@ void advanced_inventory::start_activity( const aim_location destarea, const aim_ g->u.assign_activity( ACT_WEAR ); if( by_charges ) { - if( from_vehicle ) { - g->u.activity.targets.emplace_back( vehicle_cursor( *squares[srcarea].veh, squares[srcarea].vstor ), - sitem->items.front() ); - } else { - g->u.activity.targets.emplace_back( map_cursor( squares[srcarea].pos ), sitem->items.front() ); - } + g->u.activity.targets.emplace_back( sitem->items.front() ); g->u.activity.values.push_back( amount_to_move ); } else { for( std::list::iterator it = sitem->items.begin(); amount_to_move > 0 && it != sitem->items.end(); ++it ) { - if( from_vehicle ) { - g->u.activity.targets.emplace_back( vehicle_cursor( *squares[srcarea].veh, squares[srcarea].vstor ), - *it ); - } else { - g->u.activity.targets.emplace_back( map_cursor( squares[srcarea].pos ), *it ); - } + g->u.activity.targets.emplace_back( *it ); + g->u.activity.targets.emplace_back( *it ); g->u.activity.values.push_back( 0 ); --amount_to_move; } } } else { // Find target items and quantities thereof for the new activity - std::vector target_items; + std::vector target_items; std::vector quantities; if( by_charges ) { - if( from_vehicle ) { - target_items.emplace_back( vehicle_cursor( *squares[srcarea].veh, squares[srcarea].vstor ), - sitem->items.front() ); - } else { - target_items.emplace_back( map_cursor( squares[srcarea].pos ), sitem->items.front() ); - } + target_items.emplace_back( sitem->items.front() ); + target_items.emplace_back( sitem->items.front() ); quantities.push_back( amount_to_move ); } else { for( std::list::iterator it = sitem->items.begin(); amount_to_move > 0 && it != sitem->items.end(); ++it ) { - if( from_vehicle ) { - target_items.emplace_back( vehicle_cursor( *squares[srcarea].veh, squares[srcarea].vstor ), - *it ); - } else { - target_items.emplace_back( map_cursor( squares[srcarea].pos ), *it ); - } + target_items.emplace_back( *it ); quantities.push_back( 0 ); --amount_to_move; } @@ -1251,12 +1229,12 @@ bool advanced_inventory::action_move_item( advanced_inv_listitem *sitem, if( destarea == AIM_WORN ) { g->u.assign_activity( ACT_WEAR ); - g->u.activity.targets.emplace_back( g->u, sitem->items.front() ); + g->u.activity.targets.emplace_back( sitem->items.front() ); g->u.activity.values.push_back( amount_to_move ); } else { item *itm = &g->u.i_at( sitem->idx ); - drop_locations to_move = { drop_location( item_location( g->u, itm ), amount_to_move ) }; + drop_locations to_move = { drop_location( *itm, amount_to_move ) }; g->u.assign_activity( drop_activity_actor( g->u, to_move, !to_vehicle, squares[destarea].off ) ); } // exit so that the activity can be carried out @@ -1276,7 +1254,7 @@ bool advanced_inventory::action_move_item( advanced_inv_listitem *sitem, } else if( destarea == AIM_INVENTORY ) { g->u.takeoff( *itm ); } else { - drop_locations to_move = { drop_location( item_location( g->u, itm ), amount_to_move ) }; + drop_locations to_move = { drop_location( *itm, amount_to_move ) }; g->u.assign_activity( drop_activity_actor( g->u, to_move, !to_vehicle, squares[destarea].off ) ); } // exit so that the activity can be carried out @@ -1286,7 +1264,7 @@ bool advanced_inventory::action_move_item( advanced_inv_listitem *sitem, // from map/vehicle: start ACT_PICKUP or ACT_MOVE_ITEMS as necessary // Make sure advanced inventory is reopened after activity completion. do_return_entry(); - start_activity( destarea, srcarea, sitem, amount_to_move, from_vehicle, to_vehicle ); + start_activity( destarea, sitem, amount_to_move, from_vehicle, to_vehicle ); // exit so that the activity can be carried out exit = true; @@ -1312,7 +1290,7 @@ void advanced_inventory::action_examine( advanced_inv_listitem *sitem, if( spane.get_area() == AIM_INVENTORY || spane.get_area() == AIM_WORN ) { int idx = spane.get_area() == AIM_INVENTORY ? sitem->idx : player::worn_position_to_index( sitem->idx ); - item_location loc( g->u, &g->u.i_at( idx ) ); + item *loc = &g->u.i_at( idx ); // Setup a "return to AIM" activity. If examining the item creates a new activity // (e.g. reading, reloading, activating), the new activity will be put on top of // "return to AIM". Once the new activity is finished, "return to AIM" comes back @@ -1322,7 +1300,7 @@ void advanced_inventory::action_examine( advanced_inv_listitem *sitem, do_return_entry(); assert( g->u.has_activity( ACT_ADV_INVENTORY ) ); - examine_item_menu::run( loc, info_startx, info_width, + examine_item_menu::run( *loc, info_startx, info_width, src == advanced_inventory::side::left ? examine_item_menu::menu_pos_t::left : examine_item_menu::menu_pos_t::right ); diff --git a/src/advanced_inv.h b/src/advanced_inv.h index 574cf33d1606..a4521b8c0a29 100644 --- a/src/advanced_inv.h +++ b/src/advanced_inv.h @@ -109,8 +109,7 @@ class advanced_inventory /** * a smaller chunk of display() */ - void start_activity( aim_location destarea, aim_location srcarea, - advanced_inv_listitem *sitem, int &amount_to_move, + void start_activity( aim_location destarea, advanced_inv_listitem *sitem, int &amount_to_move, bool from_vehicle, bool to_vehicle ) const; /** diff --git a/src/advanced_inv_area.cpp b/src/advanced_inv_area.cpp index dfdd5a04046d..ffd322db6afe 100644 --- a/src/advanced_inv_area.cpp +++ b/src/advanced_inv_area.cpp @@ -257,8 +257,8 @@ item *advanced_inv_area::get_container( bool in_vehicle ) // check index first if( stacks.size() > static_cast( uistate.adv_inv_container_index ) ) { auto &it = stacks[uistate.adv_inv_container_index]->front(); - if( is_container_valid( &it ) ) { - container = ⁢ + if( is_container_valid( it ) ) { + container = it; } } @@ -266,8 +266,8 @@ item *advanced_inv_area::get_container( bool in_vehicle ) if( container == nullptr ) { for( size_t x = 0; x < stacks.size(); ++x ) { auto &it = stacks[x]->front(); - if( is_container_valid( &it ) ) { - container = ⁢ + if( is_container_valid( it ) ) { + container = it; uistate.adv_inv_container_index = x; break; } @@ -279,8 +279,8 @@ item *advanced_inv_area::get_container( bool in_vehicle ) if( worn.size() > idx ) { auto iter = worn.begin(); std::advance( iter, idx ); - if( is_container_valid( &*iter ) ) { - container = &*iter; + if( is_container_valid( *iter ) ) { + container = *iter; } } @@ -288,8 +288,8 @@ item *advanced_inv_area::get_container( bool in_vehicle ) if( container == nullptr ) { auto iter = worn.begin(); for( size_t i = 0; i < worn.size(); ++i, ++iter ) { - if( is_container_valid( &*iter ) ) { - container = &*iter; + if( is_container_valid( *iter ) ) { + container = *iter; uistate.adv_inv_container_index = i; break; } @@ -433,7 +433,7 @@ advanced_inv_area::itemstack advanced_inv_area::i_stacked( T items ) std::unordered_map> cache; // iterate through and create stacks for( auto &elem : items ) { - const auto id = elem.typeId(); + const auto id = elem->typeId(); auto iter = cache.find( id ); bool got_stacked = false; // cache entry exists @@ -441,8 +441,8 @@ advanced_inv_area::itemstack advanced_inv_area::i_stacked( T items ) // check to see if it stacks with each item in a stack, not just front() for( auto &idx : iter->second ) { for( auto &it : stacks[idx] ) { - if( ( got_stacked = it->display_stacked_with( elem ) ) ) { - stacks[idx].push_back( &elem ); + if( ( got_stacked = it->display_stacked_with( *elem ) ) ) { + stacks[idx].push_back( elem ); break; } } @@ -453,7 +453,7 @@ advanced_inv_area::itemstack advanced_inv_area::i_stacked( T items ) } if( !got_stacked ) { cache[id].insert( stacks.size() ); - stacks.push_back( { &elem } ); + stacks.push_back( { elem } ); } } return stacks; diff --git a/src/advanced_inv_pane.cpp b/src/advanced_inv_pane.cpp index 997751deed47..8dc5fb0ce785 100644 --- a/src/advanced_inv_pane.cpp +++ b/src/advanced_inv_pane.cpp @@ -97,8 +97,8 @@ void advanced_inventory_pane::add_items_from_area( advanced_inv_area &square, const invslice &stacks = u.inv.slice(); for( size_t x = 0; x < stacks.size(); ++x ) { std::list item_pointers; - for( item &i : *stacks[x] ) { - item_pointers.push_back( &i ); + for( item *&i : *stacks[x] ) { + item_pointers.push_back( i ); } advanced_inv_listitem it( item_pointers, x, square.id, false ); if( is_filtered( *it.items.front() ) ) { @@ -111,7 +111,7 @@ void advanced_inventory_pane::add_items_from_area( advanced_inv_area &square, } else if( square.id == AIM_WORN ) { auto iter = u.worn.begin(); for( size_t i = 0; i < u.worn.size(); ++i, ++iter ) { - advanced_inv_listitem it( &*iter, i, 1, square.id, false ); + advanced_inv_listitem it( *iter, i, 1, square.id, false ); if( is_filtered( *it.items.front() ) ) { continue; } diff --git a/src/armor_layers.cpp b/src/armor_layers.cpp index 3ce8db87fe13..1d568c6e2da2 100644 --- a/src/armor_layers.cpp +++ b/src/armor_layers.cpp @@ -81,10 +81,11 @@ struct item_penalties { }; // Figure out encumbrance penalties this clothing is involved in -item_penalties get_item_penalties( std::list::const_iterator worn_item_it, +item_penalties get_item_penalties( ItemList::const_iterator worn_item_it, const Character &c, int tabindex ) { - layer_level layer = worn_item_it->get_layer(); + item *const &worn_item = *worn_item_it; + layer_level layer = worn_item->get_layer(); std::vector body_parts_with_stacking_penalty; std::vector body_parts_with_out_of_order_penalty; @@ -94,13 +95,13 @@ item_penalties get_item_penalties( std::list::const_iterator worn_item_it, if( bp != tabindex && num_bp != tabindex ) { continue; } - if( !worn_item_it->covers( bp ) ) { + if( !worn_item->covers( bp ) ) { continue; } const int num_items = std::count_if( c.worn.begin(), c.worn.end(), - [layer, bp]( const item & i ) { - return i.get_layer() == layer && i.covers( bp ) && !( i.has_flag( flag_SEMITANGIBLE ) || - i.has_flag( flag_COMPACT ) ); + [layer, bp]( item * const & i ) { + return i->get_layer() == layer && i->covers( bp ) && !( i->has_flag( flag_SEMITANGIBLE ) || + i->has_flag( flag_COMPACT ) ); } ); if( num_items > 1 ) { body_parts_with_stacking_penalty.push_back( bp ); @@ -108,8 +109,8 @@ item_penalties get_item_penalties( std::list::const_iterator worn_item_it, std::set bad_items_within; for( auto it = c.worn.begin(); it != worn_item_it; ++it ) { - if( it->get_layer() > layer && it->covers( bp ) ) { - bad_items_within.insert( it->type_name() ); + if( ( *it )->get_layer() > layer && ( *it )->covers( bp ) ) { + bad_items_within.insert( ( *it )->type_name() ); } } if( !bad_items_within.empty() ) { @@ -165,21 +166,22 @@ std::string body_part_names( const std::vector &parts ) } void draw_mid_pane( const catacurses::window &w_sort_middle, - std::list::const_iterator const worn_item_it, + ItemList::const_iterator const worn_item_it, const Character &c, int tabindex ) { + item *const &worn_item = *worn_item_it; const int win_width = getmaxx( w_sort_middle ); const size_t win_height = static_cast( getmaxy( w_sort_middle ) ); // NOLINTNEXTLINE(cata-use-named-point-constants) size_t i = fold_and_print( w_sort_middle, point( 1, 0 ), win_width - 1, c_white, - worn_item_it->type_name( 1 ) ) - 1; - std::vector props = clothing_properties( *worn_item_it, win_width - 3, c ); + worn_item->type_name( 1 ) ) - 1; + std::vector props = clothing_properties( *worn_item, win_width - 3, c ); nc_color color = c_light_gray; for( std::string &iter : props ) { print_colored_text( w_sort_middle, point( 2, ++i ), color, c_light_gray, iter ); } - std::vector prot = clothing_protection( *worn_item_it, win_width - 3 ); + std::vector prot = clothing_protection( *worn_item, win_width - 3 ); if( i + prot.size() < win_height ) { for( std::string &iter : prot ) { print_colored_text( w_sort_middle, point( 2, ++i ), color, c_light_gray, iter ); @@ -189,15 +191,15 @@ void draw_mid_pane( const catacurses::window &w_sort_middle, } i++; - std::vector layer_desc = foldstring( clothing_layer( *worn_item_it ), win_width ); - if( i + layer_desc.size() < win_height && !clothing_layer( *worn_item_it ).empty() ) { + std::vector layer_desc = foldstring( clothing_layer( *worn_item ), win_width ); + if( i + layer_desc.size() < win_height && !clothing_layer( *worn_item ).empty() ) { for( std::string &iter : layer_desc ) { mvwprintz( w_sort_middle, point( 0, ++i ), c_light_blue, iter ); } } i++; - std::vector desc = clothing_flags_description( *worn_item_it ); + std::vector desc = clothing_flags_description( *worn_item ); if( !desc.empty() ) { for( size_t j = 0; j < desc.size() && i + j < win_height; ++j ) { i += fold_and_print( w_sort_middle, point( 0, i ), win_width, c_light_blue, desc[j] ); @@ -208,7 +210,7 @@ void draw_mid_pane( const catacurses::window &w_sort_middle, if( !penalties.body_parts_with_stacking_penalty.empty() ) { std::string layer_description = [&]() { - switch( worn_item_it->get_layer() ) { + switch( worn_item->get_layer() ) { case PERSONAL_LAYER: return _( "in your personal aura" ); case UNDERWEAR_LAYER: @@ -405,10 +407,11 @@ static std::vector items_cover_bp( const Character &c, int b { std::vector s; for( auto elem_it = c.worn.begin(); elem_it != c.worn.end(); ++elem_it ) { - if( elem_it->covers( static_cast( bp ) ) ) { + item *const &elem = *elem_it; + if( elem->covers( static_cast( bp ) ) ) { s.push_back( { get_item_penalties( elem_it, c, bp ), - elem_it->get_encumber( c ), - elem_it->tname() + elem->get_encumber( c ), + elem->tname() } ); } } @@ -450,8 +453,8 @@ void player::sort_armor() int req_right_h = 3 + 1 + 2 + num_bp + 1; for( const body_part cover : all_body_parts ) { - for( const item &elem : worn ) { - if( elem.covers( cover ) ) { + for( item * const &elem : worn ) { + if( elem->covers( cover ) ) { req_right_h++; } } @@ -487,7 +490,8 @@ void player::sort_armor() int leftListLines = 0; int rightListLines = 0; - std::vector::iterator> tmp_worn; + //TODO!: this weird bullshit won't be necessary + std::vector tmp_worn; std::array armor_cat = {{ _( "Torso" ), _( "Head" ), _( "Eyes" ), _( "Mouth" ), _( "L. Arm" ), _( "R. Arm" ), _( "L. Hand" ), _( "R. Hand" ), _( "L. Leg" ), _( "R. Leg" ), _( "L. Foot" ), @@ -594,7 +598,7 @@ void player::sort_armor() mvwprintz( w_sort_left, point( 0, drawindex + 1 ), c_yellow, ">>" ); } - std::string worn_armor_name = tmp_worn[itemindex]->tname(); + std::string worn_armor_name = ( *tmp_worn[itemindex] )->tname(); item_penalties const penalties = get_item_penalties( tmp_worn[itemindex], *this, tabindex ); @@ -602,7 +606,7 @@ void player::sort_armor() trim_and_print( w_sort_left, point( offset_x, drawindex + 1 ), left_w - offset_x - 3, penalties.color_for_stacking_badness(), worn_armor_name ); right_print( w_sort_left, drawindex + 1, 0, c_light_gray, - format_volume( tmp_worn[itemindex]->get_storage() ) ); + format_volume( ( *tmp_worn[itemindex] )->get_storage() ) ); } // Left footer @@ -629,7 +633,7 @@ void player::sort_armor() mvwprintz( w_encumb, point_east, c_white, _( "Encumbrance and Warmth" ) ); character_display::print_encumbrance( w_encumb, *this, -1, - ( leftListSize > 0 ) ? &*tmp_worn[leftListIndex] : nullptr ); + ( leftListSize > 0 ) ? *tmp_worn[leftListIndex] : nullptr ); // Right header mvwprintz( w_sort_right, point_zero, c_light_gray, _( "(Innermost)" ) ); @@ -727,7 +731,7 @@ void player::sort_armor() // bp_* body_part bp = static_cast( tabindex ); for( auto it = worn.begin(); it != worn.end(); ++it ) { - if( it->covers( bp ) ) { + if( ( *it )->covers( bp ) ) { tmp_worn.push_back( it ); } } @@ -748,11 +752,19 @@ void player::sort_armor() // Helper function for moving items in the list auto shift_selected_item = [&]() { if( selected >= 0 ) { - std::list::iterator to = tmp_worn[leftListIndex]; - if( leftListIndex > selected ) { - ++to; - } - worn.splice( to, worn, tmp_worn[selected] ); + + + //TODO!: check this + //Can't use splice with vector + //ItemList::iterator to = tmp_worn[leftListIndex]; + //if( leftListIndex > selected ) { + // ++to; + //} + //worn.splice( to, worn, tmp_worn[selected] ); + item *temp = *tmp_worn[selected]; + *tmp_worn[selected] = *tmp_worn[leftListIndex]; + *tmp_worn[leftListIndex] = temp; + selected = leftListIndex; reset_encumbrance(); } @@ -812,44 +824,47 @@ void player::sort_armor() selected = leftListIndex; } } else if( action == "CHANGE_SIDE" ) { - if( leftListIndex < leftListSize && tmp_worn[leftListIndex]->is_sided() ) { + if( leftListIndex < leftListSize && ( *tmp_worn[leftListIndex] )->is_sided() ) { if( g->u.query_yn( _( "Swap side for %s?" ), - colorize( tmp_worn[leftListIndex]->tname(), - tmp_worn[leftListIndex]->color_in_inventory() ) ) ) { + colorize( ( *tmp_worn[leftListIndex] )->tname(), + ( *tmp_worn[leftListIndex] )->color_in_inventory() ) ) ) { change_side( *tmp_worn[leftListIndex] ); } } } else if( action == "SORT_ARMOR" ) { // Copy to a vector because stable_sort requires random-access // iterators - std::vector worn_copy( worn.begin(), worn.end() ); + //TODO!: probably don't need this anymore + std::vector worn_copy( worn.begin(), worn.end() ); std::stable_sort( worn_copy.begin(), worn_copy.end(), - []( const item & l, const item & r ) { - return l.get_layer() < r.get_layer(); + []( item * const & l, item * const & r ) { + return l->get_layer() < r->get_layer(); } ); std::copy( worn_copy.begin(), worn_copy.end(), worn.begin() ); reset_encumbrance(); } else if( action == "EQUIP_ARMOR" ) { // filter inventory for all items that are armor/clothing - item_location loc = game_menus::inv::wear( *this ); + item *loc = game_menus::inv::wear( *this ); // only equip if something valid selected! if( loc ) { // wear the item - cata::optional::iterator> new_equip_it = - wear( *loc.obtain( *this ) ); + //TODO!: check, this flow is really weird now + loc->obtain( *this ); + loc->detach(); + cata::optional new_equip_it = wear( *loc ); if( new_equip_it ) { body_part bp = static_cast( tabindex ); - if( tabindex == num_bp || ( **new_equip_it ).covers( bp ) ) { + if( tabindex == num_bp || ( **new_equip_it )->covers( bp ) ) { // Set ourselves up to be pointing at the new item // TODO: This doesn't work yet because we don't save our // state through other activities, but that's a thing // that would be nice to do. leftListIndex = std::count_if( worn.begin(), *new_equip_it, - [&]( const item & i ) { - return tabindex == num_bp || i.covers( bp ); + [&]( item * const & i ) { + return tabindex == num_bp || i->covers( bp ); } ); } } else if( is_npc() ) { @@ -859,17 +874,21 @@ void player::sort_armor() } } else if( action == "EQUIP_ARMOR_HERE" ) { // filter inventory for all items that are armor/clothing - item_location loc = game_menus::inv::wear( *this ); + item *loc = game_menus::inv::wear( *this ); // only equip if something valid selected! if( loc ) { // wear the item - if( cata::optional::iterator> new_equip_it = - wear( *loc.obtain( *this ) ) ) { + //TODO!: another weird one + loc->obtain( *this ); + loc->detach(); + + if( cata::optional new_equip_it = wear( *loc ) ) { // save iterator to cursor's position - std::list::iterator cursor_it = tmp_worn[leftListIndex]; + // ItemList::iterator cursor_it = tmp_worn[leftListIndex]; // reorder `worn` vector to place new item at cursor - worn.splice( cursor_it, worn, *new_equip_it ); + //TODO!: restore next, can't use splice with vector + //worn.splice( cursor_it, worn, *new_equip_it ); } else if( is_npc() ) { // TODO: Pass the reason here popup( _( "Can't put this on!" ) ); @@ -881,7 +900,7 @@ void player::sort_armor() if( g->u.query_yn( _( "Remove selected armor?" ) ) ) { do_return_entry(); // remove the item, asking to drop it if necessary - takeoff( *tmp_worn[leftListIndex] ); + takeoff( **tmp_worn[leftListIndex] ); if( !g->u.has_activity( ACT_ARMOR_LAYERS ) ) { // An activity has been created to take off the item; // we must surrender control until it is done. @@ -899,7 +918,7 @@ void player::sort_armor() auto witer = worn.rbegin(); while( witer != worn.rend() && iiter != inv_chars.rend() ) { const char invlet = *iiter; - item &w = *witer; + item &w = **witer; if( invlet == w.invlet ) { ++witer; } else if( invlet_to_item( invlet ) != nullptr ) { diff --git a/src/avatar.cpp b/src/avatar.cpp index eee352fd9dc0..e60409ed4ced 100644 --- a/src/avatar.cpp +++ b/src/avatar.cpp @@ -383,7 +383,7 @@ diary *avatar::get_avatar_diary() * str_values: Parallel to values, these contain the learning penalties (as doubles in string form) as follows: * Experience gained = Experience normally gained * penalty */ -bool avatar::read( item_location loc, const bool continuous ) +bool avatar::read( item *loc, const bool continuous ) { if( !loc ) { add_msg( m_info, _( "Never mind." ) ); @@ -738,7 +738,7 @@ static void skim_book_msg( const item &book, avatar &u ) add_msg( _( "You note that you have a copy of %s in your possession." ), book.type_name() ); } -void avatar::do_read( item_location loc ) +void avatar::do_read( item *loc ) { if( !loc ) { activity.set_to_null(); @@ -1231,18 +1231,18 @@ bool avatar::wield( item &target ) moves -= mv; if( has_item( target ) ) { - weapon = i_rem( &target ); + set_weapon( i_rem( &target ) ); } else { - weapon = target; + set_weapon( target ); } - last_item = weapon.typeId(); + last_item = target.typeId(); recoil = MAX_RECOIL; - weapon.on_wield( *this, mv ); + target.on_wield( *this, mv ); - inv.update_invlet( weapon ); - inv.update_cache_with_item( weapon ); + inv.update_invlet( target ); + inv.update_cache_with_item( target ); return true; } diff --git a/src/avatar.h b/src/avatar.h index b884d3c0dbc0..74b314a4a2f1 100644 --- a/src/avatar.h +++ b/src/avatar.h @@ -153,9 +153,9 @@ class avatar : public player */ int time_to_read( const item &book, const player &reader, const player *learner = nullptr ) const; /** Handles reading effects and returns true if activity started */ - bool read( item_location loc, bool continuous = false ); + bool read( item *loc, bool continuous = false ); /** Completes book reading action. **/ - void do_read( item_location loc ); + void do_read( item *loc ); /** Note that we've read a book at least once. **/ bool has_identified( const itype_id &item_id ) const override; diff --git a/src/avatar_action.cpp b/src/avatar_action.cpp index 177d0ffbb336..746af703eff9 100644 --- a/src/avatar_action.cpp +++ b/src/avatar_action.cpp @@ -89,7 +89,7 @@ static const std::string flag_SWIMMABLE( "SWIMMABLE" ); #define dbg(x) DebugLog((x), DC::SDL) bool can_fire_turret( avatar &you, const map &m, const turret_data &turret ); - +//TODO!: check bool avatar_action::move( avatar &you, map &m, const tripoint &d ) { if( ( !g->check_safe_mode_allowed() ) || you.has_active_mutation( trait_SHELL2 ) ) { @@ -127,22 +127,24 @@ bool avatar_action::move( avatar &you, map &m, const tripoint &d ) get_option( "AUTO_FEATURES" ) && get_option( "AUTO_MINING" ) && !m.veh_at( dest_loc ) && !you.is_underwater() && !you.has_effect( effect_stunned ) && !is_riding ) { - if( you.weapon.has_flag( flag_DIG_TOOL ) ) { - if( you.weapon.type->can_use( "JACKHAMMER" ) && you.weapon.ammo_sufficient() ) { - you.invoke_item( &you.weapon, "JACKHAMMER", dest_loc ); + item &weapon = you.get_weapon(); + if( weapon.has_flag( flag_DIG_TOOL ) ) { + if( weapon.type->can_use( "JACKHAMMER" ) && weapon.ammo_sufficient() ) { + you.invoke_item( &weapon, "JACKHAMMER", dest_loc ); // don't move into the tile until done mining you.defer_move( dest_loc ); return true; - } else if( you.weapon.type->can_use( "PICKAXE" ) ) { - you.invoke_item( &you.weapon, "PICKAXE", dest_loc ); + } else if( weapon.type->can_use( "PICKAXE" ) ) { + you.invoke_item( &weapon, "PICKAXE", dest_loc ); // don't move into the tile until done mining you.defer_move( dest_loc ); return true; } } if( you.has_trait( trait_BURROW ) ) { - item burrowing_item( itype_id( "fake_burrowing" ) ); - you.invoke_item( &burrowing_item, "BURROW", dest_loc ); + //TODO!: what the shit + item *burrowing_item = item::spawn_temporary( itype_id( "fake_burrowing" ) ); + you.invoke_item( burrowing_item, "BURROW", dest_loc ); // don't move into the tile until done mining you.defer_move( dest_loc ); return true; @@ -573,7 +575,7 @@ static float rate_critter( const Creature &c ) { const npc *np = dynamic_cast( &c ); if( np != nullptr ) { - return np->weapon_value( np->weapon ); + return np->weapon_value( np->get_weapon() ); } const monster *m = dynamic_cast( &c ); @@ -582,7 +584,7 @@ static float rate_critter( const Creature &c ) void avatar_action::autoattack( avatar &you, map &m ) { - int reach = you.weapon.reach_range( you ); + int reach = you.get_weapon().reach_range( you ); std::vector critters = ranged::targetable_creatures( you, reach ); critters.erase( std::remove_if( critters.begin(), critters.end(), []( const Creature * c ) { if( !c->is_npc() ) { @@ -652,7 +654,7 @@ bool avatar_action::can_fire_weapon( avatar &you, const map &m, const item &weap */ bool can_fire_turret( avatar &you, const map &m, const turret_data &turret ) { - const item &weapon = *turret.base(); + const item &weapon = turret.base(); if( !weapon.is_gun() ) { debugmsg( "Expected turret base to be a gun." ); return false; @@ -702,7 +704,7 @@ bool can_fire_turret( avatar &you, const map &m, const turret_data &turret ) void avatar_action::fire_wielded_weapon( avatar &you ) { - item &weapon = you.weapon; + item &weapon = you.get_weapon(); if( weapon.is_gunmod() ) { add_msg( m_info, _( "The %s must be attached to a gun, it can not be fired separately." ), @@ -721,12 +723,12 @@ void avatar_action::fire_wielded_weapon( avatar &you ) you.assign_activity( aim_activity_actor::use_wielded(), false ); } -void avatar_action::fire_ranged_mutation( avatar &you, const item &fake_gun ) +void avatar_action::fire_ranged_mutation( avatar &you, item &fake_gun ) { you.assign_activity( aim_activity_actor::use_mutation( fake_gun ), false ); } -void avatar_action::fire_ranged_bionic( avatar &you, const item &fake_gun, +void avatar_action::fire_ranged_bionic( avatar &you, item &fake_gun, const units::energy &cost_per_shot ) { you.assign_activity( aim_activity_actor::use_bionic( fake_gun, cost_per_shot ), false ); @@ -747,11 +749,11 @@ void avatar_action::fire_turret_manual( avatar &you, map &m, turret_data &turret g->reenter_fullscreen(); } -void avatar_action::mend( avatar &you, item_location loc ) +void avatar_action::mend( avatar &you, item *loc ) { if( !loc ) { if( you.is_armed() ) { - loc = item_location( you, &you.weapon ); + loc = &you.get_weapon(); } else { add_msg( m_info, _( "You're not wielding anything." ) ); return; @@ -759,7 +761,7 @@ void avatar_action::mend( avatar &you, item_location loc ) } if( you.has_item( *loc ) ) { - you.mend_item( item_location( loc ) ); + you.mend_item( *loc ); } } @@ -768,7 +770,7 @@ bool avatar_action::eat_here( avatar &you ) map &here = get_map(); if( ( you.has_active_mutation( trait_RUMINANT ) || you.has_active_mutation( trait_GRAZER ) ) && ( here.ter( you.pos() ) == t_underbrush || here.ter( you.pos() ) == t_shrub ) ) { - item food( itype_underbrush, calendar::turn, 1 ); + item &food = *item::spawn_temporary( itype_underbrush, calendar::turn, 1 ); if( you.get_stored_kcal() > you.max_stored_kcal() - food.get_comestible()->default_nutrition.kcal ) { add_msg( _( "You're too full to eat the leaves from the %s." ), here.ter( you.pos() )->name() ); @@ -783,7 +785,7 @@ bool avatar_action::eat_here( avatar &you ) } if( you.has_active_mutation( trait_GRAZER ) && ( here.ter( you.pos() ) == t_grass || here.ter( you.pos() ) == t_grass_long || here.ter( you.pos() ) == t_grass_tall ) ) { - item food( item( itype_grass, calendar::turn, 1 ) ); + item &food = *item::spawn_temporary( itype_grass, calendar::turn, 1 ); if( you.get_stored_kcal() > you.max_stored_kcal() - food.get_comestible()->default_nutrition.kcal ) { add_msg( _( "You're too full to graze." ) ); @@ -819,27 +821,28 @@ bool avatar_action::eat_here( avatar &you ) void avatar_action::eat( avatar &you ) { - item_location loc = game_menus::inv::consume( you ); + item *loc = game_menus::inv::consume( you ); avatar_action::eat( you, loc ); } -void avatar_action::eat( avatar &you, item_location loc ) +void avatar_action::eat( avatar &you, item *loc ) { if( !loc ) { you.cancel_activity(); add_msg( _( "Never mind." ) ); return; } - item *it = loc.get_item(); - if( loc.where() == item_location::type::character ) { - you.consume( loc ); + if( loc->where() == item::item_location_type::character ) { + you.consume( *loc ); - } else if( you.consume_item( *it ) ) { - if( it->is_food_container() || !you.can_consume_as_is( *it ) ) { - it->remove_item( it->contents.front() ); - add_msg( _( "You leave the empty %s." ), it->tname() ); + } else if( you.consume_item( *loc ) ) { + if( loc->is_food_container() || !you.can_consume_as_is( *loc ) ) { + item *content = &loc->contents.front(); + content->detach(); + content->destroy(); + add_msg( _( "You leave the empty %s." ), loc->tname() ); } else { - loc.remove_item(); + loc->destroy(); } } if( g->u.get_value( "THIEF_MODE_KEEP" ) != "YES" ) { @@ -847,7 +850,7 @@ void avatar_action::eat( avatar &you, item_location loc ) } } -void avatar_action::plthrow( avatar &you, item_location loc, +void avatar_action::plthrow( avatar &you, item *loc, const cata::optional &blind_throw_from_pos ) { if( you.has_active_mutation( trait_SHELL2 ) ) { @@ -877,7 +880,8 @@ void avatar_action::plthrow( avatar &you, item_location loc, // make a copy and get the original. // the copy is thrown and has its and the originals charges set appropiately // or deleted from inventory if its charges(1) or not stackable. - item thrown = *loc.get_item(); + //TODO!: no more of that shit + item &thrown = *item::spawn( *loc ); int range = you.throw_range( thrown ); if( range < 0 ) { add_msg( m_info, _( "You don't have that item." ) ); @@ -914,7 +918,7 @@ void avatar_action::plthrow( avatar &you, item_location loc, // But only if you don't have enough free hands int usable_hands = you.get_working_arm_count() - ( you.is_armed() ? 1 : 0 ) - - ( you.weapon.is_two_handed( you ) ? 1 : 0 ); + ( you.get_weapon().is_two_handed( you ) ? 1 : 0 ); if( !you.is_wielding( *loc ) && ( usable_hands < ( loc->is_two_handed( you ) ? 2 : 1 ) ) ) { if( !you.wield( *loc ) ) { @@ -923,21 +927,21 @@ void avatar_action::plthrow( avatar &you, item_location loc, return; } - loc = item_location( you, &you.weapon ); + loc = &you.get_weapon(); } - throw_activity_actor actor( loc, blind_throw_from_pos ); + throw_activity_actor actor( *loc, blind_throw_from_pos ); you.assign_activity( actor, false ); } -static void make_active( item_location loc ) +static void make_active( item& loc ) { map &here = get_map(); switch( loc.where() ) { - case item_location::type::map: + case item::item_location_type::map: here.make_active( loc ); break; - case item_location::type::vehicle: + case item::item_location_type::vehicle: here.veh_at( loc.position() )->vehicle().make_active( loc ); break; default: @@ -945,10 +949,10 @@ static void make_active( item_location loc ) } } -static void update_lum( item_location loc, bool add ) +static void update_lum( item& loc, bool add ) { switch( loc.where() ) { - case item_location::type::map: + case item::item_location_type::map: get_map().update_lum( loc, add ); break; default: @@ -956,13 +960,7 @@ static void update_lum( item_location loc, bool add ) } } -void avatar_action::use_item( avatar &you ) -{ - item_location loc; - avatar_action::use_item( you, loc ); -} - -void avatar_action::use_item( avatar &you, item_location &loc ) +void avatar_action::use_item( avatar &you, item* loc ) { // Some items may be used without being picked up first bool use_in_place = false; @@ -978,12 +976,9 @@ void avatar_action::use_item( avatar &you, item_location &loc ) if( loc->has_flag( flag_ALLOWS_REMOTE_USE ) ) { use_in_place = true; } else { - const int obtain_cost = loc.obtain_cost( you ); - loc = loc.obtain( you ); - if( !loc ) { - debugmsg( "Failed to obtain target item" ); - return; - } + const int obtain_cost = loc->obtain_cost( you ); + //TODO!: check + loc->obtain( you ); // TODO: the following comment is inaccurate and this mechanic needs to be rexamined // This method only handles items in the inventory, so refund the obtain cost. @@ -992,13 +987,13 @@ void avatar_action::use_item( avatar &you, item_location &loc ) } if( use_in_place ) { - update_lum( loc, false ); - you.use( loc ); - update_lum( loc, true ); + update_lum( *loc, false ); + you.use( *loc ); + update_lum( *loc, true ); - make_active( loc ); + make_active( *loc ); } else { - you.use( loc ); + you.use( *loc ); } you.invalidate_crafting_inventory(); @@ -1006,22 +1001,22 @@ void avatar_action::use_item( avatar &you, item_location &loc ) void avatar_action::wield() { - item_location loc = game_menus::inv::wield( get_avatar() ); + item* loc = game_menus::inv::wield( get_avatar() ); if( loc ) { - wield( loc ); + wield( *loc ); } else { add_msg( _( "Never mind." ) ); } } - -void avatar_action::wield( item_location &loc ) +//TODO!: check pointers +void avatar_action::wield( item &loc ) { avatar &u = get_avatar(); map &here = get_map(); if( u.is_armed() ) { - const bool is_unwielding = u.is_wielding( *loc ); - const auto ret = u.can_unwield( *loc ); + const bool is_unwielding = u.is_wielding( loc ); + const auto ret = u.can_unwield( loc ); if( !ret.success() ) { add_msg( m_info, "%s", ret.c_str() ); @@ -1037,13 +1032,13 @@ void avatar_action::wield( item_location &loc ) } } - const auto ret = u.can_wield( *loc ); + const auto ret = u.can_wield( loc ); if( !ret.success() ) { add_msg( m_info, "%s", ret.c_str() ); } // Need to do this here because holster_actor::use() checks if/where the item is worn - item &target = *loc.get_item(); + item &target = loc; if( target.get_use( "holster" ) && !target.contents.empty() ) { //~ %1$s: weapon name, %2$s: holster name if( query_yn( pgettext( "holster", "Draw %1$s from %2$s?" ), target.get_contained().tname(), @@ -1054,50 +1049,51 @@ void avatar_action::wield( item_location &loc ) } // Can't use loc.obtain() here because that would cause things to spill. - item to_wield = *loc.get_item(); - item_location::type location_type = loc.where(); + item *to_wield = &loc; + item::item_location_type location_type = loc.where(); tripoint pos = loc.position(); int worn_index = INT_MIN; - if( u.is_worn( *loc.get_item() ) ) { - auto ret = u.can_takeoff( *loc.get_item() ); + if( u.is_worn( loc ) ) { + auto ret = u.can_takeoff( loc ); if( !ret.success() ) { add_msg( m_info, "%s", ret.c_str() ); return; } - int item_pos = u.get_item_position( loc.get_item() ); + int item_pos = u.get_item_position( &loc ); if( item_pos != INT_MIN ) { worn_index = Character::worn_position_to_index( item_pos ); } } - loc.remove_item(); - if( !u.wield( to_wield ) ) { + loc.detach(); + if( !u.wield( *to_wield ) ) { switch( location_type ) { - case item_location::type::container: + case item::item_location_type::container: // this will not cause things to spill, as it is inside another item - loc = loc.obtain( u ); + loc.obtain( u ); + //TODO!: check location stuff, this may debugmsg wield( loc ); break; - case item_location::type::character: + case item::item_location_type::character: if( worn_index != INT_MIN ) { auto it = u.worn.begin(); std::advance( it, worn_index ); u.worn.insert( it, to_wield ); } else { - u.i_add( to_wield ); + u.i_add( *to_wield ); } break; - case item_location::type::map: - here.add_item( pos, to_wield ); + case item::item_location_type::map: + here.add_item( pos, *to_wield ); break; - case item_location::type::vehicle: { + case item::item_location_type::vehicle: { const cata::optional vp = here.veh_at( pos ).part_with_feature( "CARGO", false ); // If we fail to return the item to the vehicle for some reason, add it to the map instead. - if( !vp || !( vp->vehicle().add_item( vp->part_index(), to_wield ) ) ) { - here.add_item( pos, to_wield ); + if( !vp || !( vp->vehicle().add_item( vp->part_index(), *to_wield ) ) ) { + here.add_item( pos, *to_wield ); } break; } - case item_location::type::invalid: + case item::item_location_type::invalid: debugmsg( "Failed wield from invalid item location" ); break; } @@ -1108,7 +1104,7 @@ void avatar_action::wield( item_location &loc ) static item::reload_option favorite_ammo_or_select( const player &u, const item &it, bool empty, bool prompt ) { - const_cast( u.ammo_location ).make_dirty(); + //TODO!: check if( u.ammo_location ) { std::vector ammo_list; if( u.list_ammo( it, ammo_list, empty ) ) { @@ -1120,8 +1116,6 @@ static item::reload_option favorite_ammo_or_select( return *iter; } } - } else { - const_cast( u.ammo_location ) = item_location(); } return u.select_ammo( it, prompt, empty ); } @@ -1137,11 +1131,10 @@ static bool can_reload_item_or_mods( const avatar &you, const item &itm ) } -void avatar_action::reload( item_location &loc, bool prompt, bool empty ) +void avatar_action::reload( item &loc, bool prompt, bool empty ) { avatar &u = get_avatar(); - u.ammo_location.make_dirty(); - item *it = loc.get_item(); + item *it = &loc; // bows etc. do not need to reload. select favorite ammo for them instead if( it->has_flag( "RELOAD_AND_SHOOT" ) ) { @@ -1173,7 +1166,7 @@ void avatar_action::reload( item_location &loc, bool prompt, bool empty ) bool use_loc = true; if( !it->has_flag( "ALLOWS_REMOTE_USE" ) ) { - it = loc.obtain( u ).get_item(); + loc.obtain( u ); use_loc = false; } @@ -1192,7 +1185,7 @@ void avatar_action::reload( item_location &loc, bool prompt, bool empty ) item::reload_option opt = favorite_ammo_or_select( u, *it, empty, prompt ); - if( opt.ammo.get_item() == nullptr ) { + if( opt.ammo == nullptr ) { return; } @@ -1207,7 +1200,7 @@ void avatar_action::reload( item_location &loc, bool prompt, bool empty ) if( use_loc ) { u.activity.targets.emplace_back( loc ); } else { - u.activity.targets.emplace_back( u, const_cast( opt.target ) ); + u.activity.targets.emplace_back( opt.target ); } u.activity.targets.push_back( std::move( opt.ammo ) ); } @@ -1215,7 +1208,7 @@ void avatar_action::reload( item_location &loc, bool prompt, bool empty ) void avatar_action::reload_item() { - item_location item_loc = g->inv_map_splice( []( const item & it ) { + item* item_loc = g->inv_map_splice( []( const item & it ) { return can_reload_item_or_mods( get_avatar(), it ); }, _( "Reload item" ), 1, _( "You have nothing to reload." ) ); @@ -1224,18 +1217,17 @@ void avatar_action::reload_item() return; } - reload( item_loc ); + reload( *item_loc ); } void avatar_action::reload_wielded( bool prompt ) { avatar &u = get_avatar(); - if( u.weapon.is_null() || !u.weapon.is_reloadable() ) { + if( u.get_weapon().is_null() || !u.get_weapon().is_reloadable() ) { add_msg( _( "You aren't holding something you can reload." ) ); return; } - item_location item_loc = item_location( u, &u.weapon ); - reload( item_loc, prompt ); + reload( u.get_weapon(), prompt ); } void avatar_action::reload_weapon( bool try_everything ) @@ -1249,11 +1241,9 @@ void avatar_action::reload_weapon( bool try_everything ) // Reload misc magazines in inventory. avatar &u = get_avatar(); map &here = get_map(); - std::vector reloadables = u.find_reloadables(); + std::vector reloadables = u.find_reloadables(); std::sort( reloadables.begin(), reloadables.end(), - [&u]( const item_location & a, const item_location & b ) { - const item *ap = a.get_item(); - const item *bp = b.get_item(); + [&u]( item* ap, item* bp ) { // Current wielded weapon comes first. if( u.is_wielding( *bp ) ) { return false; @@ -1262,7 +1252,7 @@ void avatar_action::reload_weapon( bool try_everything ) return true; } // Second sort by affiliation with wielded gun - const std::set compatible_magazines = u.weapon.magazine_compatible(); + const std::set compatible_magazines = u.get_weapon().magazine_compatible(); const bool mag_ap = compatible_magazines.count( ap->typeId() ) > 0; const bool mag_bp = compatible_magazines.count( bp->typeId() ) > 0; if( mag_ap != mag_bp ) { @@ -1276,11 +1266,11 @@ void avatar_action::reload_weapon( bool try_everything ) return ( ap->get_reload_time() * ( ap->ammo_capacity() - ap->ammo_remaining() ) ) < ( bp->get_reload_time() * ( bp->ammo_capacity() - bp->ammo_remaining() ) ); } ); - for( item_location &candidate : reloadables ) { + for( item* &candidate : reloadables ) { std::vector ammo_list; - u.list_ammo( *candidate.get_item(), ammo_list, false ); + u.list_ammo( *candidate, ammo_list, false ); if( !ammo_list.empty() ) { - reload( candidate, false, false ); + reload( *candidate, false, false ); return; } } @@ -1292,7 +1282,7 @@ void avatar_action::reload_weapon( bool try_everything ) vehicle *veh = veh_pointer_or_null( here.veh_at( u.pos() ) ); turret_data turret; if( veh && ( turret = veh->turret_query( u.pos() ) ) && turret.can_reload() ) { - item::reload_option opt = u.select_ammo( *turret.base(), true ); + item::reload_option opt = u.select_ammo( turret.base(), true ); if( opt ) { u.assign_activity( activity_id( "ACT_RELOAD" ), opt.moves(), opt.qty() ); u.activity.targets.emplace_back( turret.base() ); @@ -1306,7 +1296,7 @@ void avatar_action::reload_weapon( bool try_everything ) void avatar_action::unload( avatar &you ) { - item_location loc = g->inv_map_splice( []( const item & it ) { + item* loc = g->inv_map_splice( []( const item & it ) { return item_funcs::can_be_unloaded( it ); }, _( "Unload item" ), 1, _( "You have nothing to unload." ) ); @@ -1315,5 +1305,5 @@ void avatar_action::unload( avatar &you ) return; } - you.unload( loc ); + you.unload( *loc ); } diff --git a/src/avatar_action.h b/src/avatar_action.h index 8e5e1a304390..a94faf36f043 100644 --- a/src/avatar_action.h +++ b/src/avatar_action.h @@ -9,7 +9,6 @@ class avatar; class Character; class item; -class item_location; class map; class turret_data; @@ -18,7 +17,7 @@ namespace avatar_action /** Eat food or fuel 'E' (or 'a') */ void eat( avatar &you ); -void eat( avatar &you, item_location loc ); +void eat( avatar &you, item *loc ); // special rules for eating: grazing etc // returns false if no rules are needed bool eat_here( avatar &you ); @@ -39,15 +38,15 @@ void swim( map &m, avatar &you, const tripoint &p ); void autoattack( avatar &you, map &m ); -void mend( avatar &you, item_location loc ); +void mend( avatar &you, item *loc ); /** Prompt to wield some item. */ void wield(); /** Wield specified item. */ -void wield( item_location &loc ); +void wield( item &loc ); /** Reload specified item. */ -void reload( item_location &loc, bool prompt = false, bool empty = true ); +void reload( item &loc, bool prompt = false, bool empty = true ); /** Prompt to reload some item. */ void reload_item(); /** Reload wielded item. */ @@ -74,10 +73,10 @@ bool can_fire_weapon( avatar &you, const map &m, const item &weapon ); void fire_wielded_weapon( avatar &you ); /** Stores fake gun specified by the mutation and starts interactive aiming */ -void fire_ranged_mutation( avatar &you, const item &fake_gun ); +void fire_ranged_mutation( avatar &you, item &fake_gun ); /** Stores fake gun specified by the bionic and starts interactive aiming */ -void fire_ranged_bionic( avatar &you, const item &fake_gun, const units::energy &cost_per_shot ); +void fire_ranged_bionic( avatar &you, item &fake_gun, const units::energy &cost_per_shot ); /** * Checks if the player can manually (with their 2 hands, not via vehicle controls) @@ -87,12 +86,11 @@ void fire_ranged_bionic( avatar &you, const item &fake_gun, const units::energy void fire_turret_manual( avatar &you, map &m, turret_data &turret ); // Throw an item 't' -void plthrow( avatar &you, item_location loc, +void plthrow( avatar &you, item *loc, const cata::optional &blind_throw_from_pos = cata::nullopt ); // Use item; also tries E,R,W 'a' -void use_item( avatar &you, item_location &loc ); -void use_item( avatar &you ); +void use_item( avatar &you, item *loc=nullptr ); } // namespace avatar_action #endif // CATA_SRC_AVATAR_ACTION_H diff --git a/src/ballistics.cpp b/src/ballistics.cpp index 2e99bb31ad8a..bee167fff6b6 100644 --- a/src/ballistics.cpp +++ b/src/ballistics.cpp @@ -59,7 +59,7 @@ static const std::string flag_LIQUID( "LIQUID" ); static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) { const auto &proj = attack.proj; - const item &drop_item = proj.get_drop(); + item &drop_item = proj.get_drop(); if( drop_item.is_null() ) { return; } @@ -73,7 +73,8 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) } // copies the drop item to spill the contents - item( drop_item ).spill_contents( pt ); + //TODO!: check why, cos we ain't doing that no more I guess + drop_item.spill_contents( pt ); // TODO: Non-glass breaking // TODO: Wine glass breaking vs. entire sheet of glass breaking @@ -89,14 +90,16 @@ static void drop_or_embed_projectile( const dealt_projectile_attack &attack ) } // copies the drop item to spill the contents - item( drop_item ).spill_contents( pt ); + //TODO!: again check why, cos we ain't doing that no more I guess + drop_item.spill_contents( pt ); // TODO: Sound return; } // Copy the item - item dropped_item = drop_item; + //TODO!: whyyyy check alla this + item &dropped_item = drop_item; monster *mon = dynamic_cast( attack.hit_critter ); diff --git a/src/basecamp.cpp b/src/basecamp.cpp index 246214cc848e..a2aa37139a05 100644 --- a/src/basecamp.cpp +++ b/src/basecamp.cpp @@ -395,7 +395,7 @@ void basecamp::add_resource( const itype_id &camp_resource ) { basecamp_resource bcp_r; bcp_r.fake_id = camp_resource; - item camp_item( bcp_r.fake_id, calendar::start_of_cataclysm ); + item &camp_item = *item::spawn( bcp_r.fake_id, calendar::start_of_cataclysm ); bcp_r.ammo_id = camp_item.ammo_default(); resources.emplace_back( bcp_r ); fuel_types.insert( bcp_r.ammo_id ); @@ -581,20 +581,20 @@ void basecamp::set_name( const std::string &new_name ) * we could put this logic in map::use_charges() the way the vehicle code does, but I think * that's sloppy */ -std::list basecamp::use_charges( const itype_id &fake_id, int &quantity ) +ItemList basecamp::use_charges( const itype_id &fake_id, int &quantity ) { - std::list ret; + ItemList ret; if( quantity <= 0 ) { return ret; } for( basecamp_resource &bcp_r : resources ) { if( bcp_r.fake_id == fake_id ) { - item camp_item( bcp_r.fake_id, calendar::start_of_cataclysm ); + item &camp_item = *item::spawn( bcp_r.fake_id, calendar::start_of_cataclysm ); camp_item.charges = std::min( bcp_r.available, quantity ); quantity -= camp_item.charges; bcp_r.available -= camp_item.charges; bcp_r.consumed += camp_item.charges; - ret.push_back( camp_item ); + ret.push_back( &camp_item ); if( quantity <= 0 ) { break; } @@ -634,10 +634,10 @@ void basecamp::form_crafting_inventory( map &target_map ) // find available fuel for( const tripoint &pt : target_map.points_in_radius( origin, inv_range ) ) { if( target_map.accessible_items( pt ) ) { - for( const item &i : target_map.i_at( pt ) ) { + for( const item * const &i : target_map.i_at( pt ) ) { for( basecamp_fuel &bcp_f : fuels ) { - if( bcp_f.ammo_id == i.typeId() ) { - bcp_f.available += i.charges; + if( bcp_f.ammo_id == i->typeId() ) { + bcp_f.available += i->charges; break; } } @@ -646,14 +646,15 @@ void basecamp::form_crafting_inventory( map &target_map ) } for( basecamp_resource &bcp_r : resources ) { bcp_r.consumed = 0; - item camp_item( bcp_r.fake_id, calendar::start_of_cataclysm ); + //TODO!: more check of basecamp weird + item &camp_item = *item::spawn( bcp_r.fake_id, calendar::start_of_cataclysm ); camp_item.set_flag( "PSEUDO" ); if( !bcp_r.ammo_id.is_null() ) { for( basecamp_fuel &bcp_f : fuels ) { if( bcp_f.ammo_id == bcp_r.ammo_id ) { if( bcp_f.available > 0 ) { bcp_r.available = bcp_f.available; - camp_item = camp_item.ammo_set( bcp_f.ammo_id, bcp_f.available ); + camp_item.ammo_set( bcp_f.ammo_id, bcp_f.available ); } break; } diff --git a/src/basecamp.h b/src/basecamp.h index fc5649fffa5f..7ceabb1c3d84 100644 --- a/src/basecamp.h +++ b/src/basecamp.h @@ -207,7 +207,7 @@ class basecamp int recipe_batch_max( const recipe &making ) const; void form_crafting_inventory(); void form_crafting_inventory( map &target_map ); - std::list use_charges( const itype_id &fake_id, int &quantity ); + ItemList use_charges( const itype_id &fake_id, int &quantity ); item_group_id get_gatherlist() const; /** * spawn items or corpses based on search attempts @@ -235,7 +235,7 @@ class basecamp inline void set_dumping_spot( const tripoint &spot ) { dumping_spot = spot; } - void place_results( item result ); + void place_results( item &result ); // mission description functions void add_available_recipes( mission_data &mission_key, const point &dir, diff --git a/src/bionics.cpp b/src/bionics.cpp index 3dcdc70e9e36..57c18884e6cb 100644 --- a/src/bionics.cpp +++ b/src/bionics.cpp @@ -455,7 +455,7 @@ void npc::discharge_cbm_weapon() } const bionic &bio = ( *my_bionics )[cbm_weapon_index]; mod_power_level( -bio.info().power_activate ); - weapon = real_weapon; + set_weapon(*real_weapon); cbm_weapon_index = -1; } @@ -486,36 +486,38 @@ void npc::check_or_use_weapon_cbm( const bionic_id &cbm_id ) bionic &bio = ( *my_bionics )[index]; if( bio.info().has_flag( flag_BIONIC_GUN ) ) { - const item cbm_weapon = item( bio.info().fake_item ); + //TODO!: restore next + item *cbm_weapon = nullptr;//item( bio.info().fake_item ); bool not_allowed = !rules.has_flag( ally_rule::use_guns ) || - ( rules.has_flag( ally_rule::use_silent ) && !cbm_weapon.is_silent() ); + ( rules.has_flag( ally_rule::use_silent ) && !cbm_weapon->is_silent() ); if( is_player_ally() && not_allowed ) { return; } const int ups_charges = charges_of( itype_UPS ); - int ammo_count = weapon.ammo_remaining(); - const int ups_drain = weapon.get_gun_ups_drain(); + int ammo_count = get_weapon().ammo_remaining(); + const int ups_drain = get_weapon().get_gun_ups_drain(); if( ups_drain > 0 ) { ammo_count = std::min( ammo_count, ups_charges / ups_drain ); } const int cbm_ammo = free_power / bio.info().power_activate; - if( weapon_value( weapon, ammo_count ) < weapon_value( cbm_weapon, cbm_ammo ) ) { - real_weapon = weapon; - weapon = cbm_weapon; + if( weapon_value( get_weapon(), ammo_count ) < weapon_value( *cbm_weapon, cbm_ammo ) ) { + real_weapon = &get_weapon(); + set_weapon( *cbm_weapon); cbm_weapon_index = index; } - } else if( bio.info().has_flag( flag_BIONIC_WEAPON ) && !weapon.has_flag( flag_NO_UNWIELD ) && + } else if( bio.info().has_flag( flag_BIONIC_WEAPON ) && !get_weapon().has_flag( flag_NO_UNWIELD ) && free_power > bio.info().power_activate ) { if( is_armed() ) { - stow_item( weapon ); + stow_item( get_weapon() ); } if( get_player_character().sees( pos() ) ) { add_msg( m_info, _( "%s activates their %s." ), disp_name(), bio.info().name ); } - weapon = item( bio.info().fake_item ); + //TODO!: restore next + //weapon = item( bio.info().fake_item ); mod_power_level( -bio.info().power_activate ); bio.powered = true; cbm_weapon_index = index; @@ -581,7 +583,6 @@ bool Character::activate_bionic( int b, bool eff_only ) } }; - item tmp_item; const w_point &weatherPoint = get_weather().get_precise(); map &here = get_map(); @@ -589,19 +590,20 @@ bool Character::activate_bionic( int b, bool eff_only ) if( bio.info().has_flag( flag_BIONIC_GUN ) ) { add_msg_activate(); refund_power(); // Power usage calculated later, in avatar_action::fire - avatar_action::fire_ranged_bionic( *this->as_avatar(), item( bio.info().fake_item ), + //TODO!: check lifetime + avatar_action::fire_ranged_bionic( *this->as_avatar(), *item::spawn( bio.info().fake_item ), bio.info().power_activate ); } else if( bio.info().has_flag( flag_BIONIC_WEAPON ) ) { - if( weapon.has_flag( flag_NO_UNWIELD ) ) { - add_msg_if_player( m_info, _( "Deactivate your %s first!" ), weapon.tname() ); + if( weapon->has_flag( flag_NO_UNWIELD ) ) { + add_msg_if_player( m_info, _( "Deactivate your %s first!" ), weapon->tname() ); refund_power(); bio.powered = false; return false; } - if( !weapon.is_null() ) { - const std::string query = string_format( _( "Stop wielding %s?" ), weapon.tname() ); - if( !dispose_item( item_location( *this, &weapon ), query ) ) { + if( !weapon->is_null() ) { + const std::string query = string_format( _( "Stop wielding %s?" ), weapon->tname() ); + if( !dispose_item( *weapon , query ) ) { refund_power(); bio.powered = false; return false; @@ -609,10 +611,11 @@ bool Character::activate_bionic( int b, bool eff_only ) } add_msg_activate(); - weapon = item( bio.info().fake_item ); - weapon.invlet = '#'; + //TODO!: restore next + //weapon = item( bio.info().fake_item ); + weapon->invlet = '#'; if( bio.ammo_count > 0 ) { - weapon.ammo_set( bio.ammo_loaded, bio.ammo_count ); + weapon->ammo_set( bio.ammo_loaded, bio.ammo_count ); avatar_action::fire_wielded_weapon( g->u ); } } else if( bio.id == bio_ears && has_active_bionic( bio_earplugs ) ) { @@ -787,19 +790,19 @@ bool Character::activate_bionic( int b, bool eff_only ) } else if( bio.id == bio_water_extractor ) { bool no_target = true; bool extracted = false; - for( item &it : here.i_at( pos() ) ) { + for( item *&it : here.i_at( pos() ) ) { static const auto volume_per_water_charge = 500_ml; - if( it.is_corpse() ) { - const int avail = it.get_var( "remaining_water", it.volume() / volume_per_water_charge ); + if( it->is_corpse() ) { + const int avail = it->get_var( "remaining_water", it->volume() / volume_per_water_charge ); if( avail > 0 ) { no_target = false; if( query_yn( _( "Extract water from the %s" ), - colorize( it.tname(), it.color_in_inventory() ) ) ) { - item water( itype_water_clean, calendar::turn, avail ); + colorize( it->tname(), it->color_in_inventory() ) ) ) { + item &water = *item::spawn( itype_water_clean, calendar::turn, avail ); if( liquid_handler::consume_liquid( water ) ) { add_msg_activate(); extracted = true; - it.set_var( "remaining_water", static_cast( water.charges ) ); + it->set_var( "remaining_water", static_cast( water.charges ) ); } break; } @@ -819,7 +822,7 @@ bool Character::activate_bionic( int b, bool eff_only ) { material_id( "iron" ), material_id( "steel" ) }; // Remember all items that will be affected, then affect them // Don't "snowball" by affecting some items multiple times - std::vector> affected; + std::vector> affected; const units::mass weight_cap = weight_capacity(); for( const tripoint &p : here.points_in_radius( pos(), 10 ) ) { if( p == pos() || !here.has_items( p ) || here.has_flag( flag_SEALED, p ) ) { @@ -828,8 +831,8 @@ bool Character::activate_bionic( int b, bool eff_only ) map_stack stack = here.i_at( p ); for( auto it = stack.begin(); it != stack.end(); it++ ) { - if( it->weight() < weight_cap && - it->made_of_any( affected_materials ) ) { + if( ( *it )->weight() < weight_cap && + ( *it )->made_of_any( affected_materials ) ) { affected.emplace_back( std::make_pair( *it, p ) ); stack.erase( it ); break; @@ -837,10 +840,10 @@ bool Character::activate_bionic( int b, bool eff_only ) } } - for( const std::pair &pr : affected ) { + for( const std::pair &pr : affected ) { projectile proj; proj.speed = 50; - proj.impact = damage_instance::physical( pr.first.weight() / 250_gram, 0, 0, 0 ); + proj.impact = damage_instance::physical( pr.first->weight() / 250_gram, 0, 0, 0 ); // make the projectile stop one tile short to prevent hitting the player proj.range = rl_dist( pr.second, pos() ) - 1; static const std::set ammo_effects = {{ @@ -856,7 +859,7 @@ bool Character::activate_bionic( int b, bool eff_only ) dealt_projectile_attack dealt = projectile_attack( proj, pr.second, pos(), dispersion_sources{ 0 }, this ); - here.add_item_or_charges( dealt.end_point, pr.first ); + here.add_item_or_charges( dealt.end_point, *pr.first ); } mod_moves( -100 ); @@ -948,16 +951,16 @@ bool Character::activate_bionic( int b, bool eff_only ) _( "Control vehicle" ), _( "RC radio" ) } ); if( choice >= 0 && choice <= 1 ) { - item ctr; + item *ctr; if( choice == 0 ) { - ctr = item( "remotevehcontrol", calendar::start_of_cataclysm ); + ctr = item::spawn_temporary( "remotevehcontrol", calendar::start_of_cataclysm ); } else { - ctr = item( "radiocontrol", calendar::start_of_cataclysm ); + ctr = item::spawn_temporary( "radiocontrol", calendar::start_of_cataclysm ); } - ctr.charges = units::to_kilojoule( get_power_level() ); - int power_use = invoke_item( &ctr ); + ctr->charges = units::to_kilojoule( get_power_level() ); + int power_use = invoke_item( ctr ); mod_power_level( units::from_kilojoule( -power_use ) ); - bio.powered = ctr.active; + bio.powered = ctr->active; } else { bio.powered = g->remoteveh() != nullptr || !get_value( "remote_controlling" ).empty(); } @@ -1102,16 +1105,16 @@ bool Character::deactivate_bionic( int b, bool eff_only ) // Deactivation effects go here if( bio.info().has_flag( flag_BIONIC_WEAPON ) ) { - if( weapon.typeId() == bio.info().fake_item ) { - add_msg_if_player( _( "You withdraw your %s." ), weapon.tname() ); + if( weapon->typeId() == bio.info().fake_item ) { + add_msg_if_player( _( "You withdraw your %s." ), weapon->tname() ); if( g->u.sees( pos() ) ) { add_msg_if_npc( m_info, _( " withdraws %s %s." ), disp_name( true ), - weapon.tname() ); + weapon->tname() ); } bio.ammo_loaded = - weapon.ammo_data() != nullptr ? weapon.ammo_data()->get_id() : itype_id::NULL_ID(); - bio.ammo_count = static_cast( weapon.ammo_remaining() ); - weapon = item(); + weapon->ammo_data() != nullptr ? weapon->ammo_data()->get_id() : itype_id::NULL_ID(); + bio.ammo_count = static_cast( weapon->ammo_remaining() ); + weapon = nullptr; invalidate_crafting_inventory(); } } else if( bio.id == bio_cqb ) { @@ -1194,7 +1197,7 @@ bool Character::burn_fuel( int b, bool start ) //in the menu if( !start ) { for( const itype_id &fuel : fuel_available ) { - const item &tmp_fuel = item( fuel ); + const item &tmp_fuel = *item::spawn_temporary( fuel ); const int fuel_energy = tmp_fuel.fuel_energy(); const bool is_perpetual_fuel = tmp_fuel.has_flag( flag_PERPETUAL ); @@ -1321,9 +1324,8 @@ void Character::passive_power_gen( int b ) map &here = get_map(); for( const itype_id &fuel : fuel_available ) { - const item &tmp_fuel = item( fuel ); - const int fuel_energy = tmp_fuel.fuel_energy(); - if( !tmp_fuel.has_flag( flag_PERPETUAL ) ) { + const int fuel_energy = fuel->fuel ? fuel->fuel->energy : 0.0f; + if( !fuel->has_flag( flag_PERPETUAL ) ) { continue; } @@ -1444,7 +1446,8 @@ int Character::consume_remote_fuel( int amount ) void Character::reset_remote_fuel() { - if( get_bionic_fueled_with( item( fuel_type_sun_light ) ).empty() ) { + //TOOD!: check what the shit + if( get_bionic_fueled_with( *item::spawn_temporary( fuel_type_sun_light ) ).empty() ) { remove_value( "sunlight" ); } remove_value( "rem_battery" ); @@ -1480,11 +1483,11 @@ float Character::get_effective_efficiency( int b, float fuel_efficiency ) int coverage = 0; const std::map< bodypart_str_id, int > &occupied_bodyparts = bio.info().occupied_bodyparts; for( const std::pair< const bodypart_str_id, int > &elem : occupied_bodyparts ) { - for( const item &i : worn ) { - if( i.covers( elem.first->token ) && !i.has_flag( flag_ALLOWS_NATURAL_ATTACKS ) && - !i.has_flag( flag_SEMITANGIBLE ) && - !i.has_flag( flag_PERSONAL ) && !i.has_flag( flag_AURA ) ) { - coverage += i.get_coverage(); + for( item * const &i : worn ) { + if( i->covers( elem.first->token ) && !i->has_flag( flag_ALLOWS_NATURAL_ATTACKS ) && + !i->has_flag( flag_SEMITANGIBLE ) && + !i->has_flag( flag_PERSONAL ) && !i->has_flag( flag_AURA ) ) { + coverage += i->get_coverage(); } } } @@ -1536,8 +1539,7 @@ void Character::process_bionic( int b ) if( !rem_amount.empty() ) { rem_fuel_stock = std::stoi( rem_amount ); } - if( !rem_fuel.is_empty() && ( rem_fuel_stock > 0 || - item( rem_fuel ).has_flag( flag_PERPETUAL ) ) ) { + if( !rem_fuel.is_empty() && ( rem_fuel_stock > 0 || rem_fuel->has_flag( flag_PERPETUAL ) ) ) { fuel_available.emplace_back( rem_fuel ); } } @@ -2061,12 +2063,14 @@ void Character::perform_uninstall( bionic_id bid, int difficulty, int success, // remove power bank provided by bionic mod_max_power_level( -power_lvl ); - item cbm( itype_burnt_out_bionic ); + item *cbm; if( bid->itype().is_valid() ) { - cbm = item( bid.c_str() ); + cbm = item::spawn( bid.c_str() ); + } else { + cbm = item::spawn( itype_burnt_out_bionic ); } - cbm.faults.emplace( fault_bionic_nonsterile ); - here.add_item( pos(), cbm ); + cbm->faults.emplace( fault_bionic_nonsterile ); + here.add_item( pos(), *cbm ); } else { g->events().send( getID(), bid ); // for chance_of_success calculation, shift skill down to a float between ~0.4 - 30 @@ -2136,7 +2140,7 @@ bool Character::uninstall_bionic( const bionic &target_cbm, monster &installer, patient.mod_max_power_level( -target_cbm.info().capacity ); patient.remove_bionic( target_cbm.id ); const itype_id iid = itemtype.is_valid() ? itemtype : itype_burnt_out_bionic; - item cbm( iid, calendar::start_of_cataclysm ); + item &cbm = *item::spawn( iid, calendar::start_of_cataclysm ); cbm.faults.emplace( fault_bionic_nonsterile ); get_map().add_item( patient.pos(), cbm ); } else { @@ -2681,7 +2685,8 @@ int bionic::get_quality( const quality_id &quality ) const return INT_MIN; } - return item( i.fake_item ).get_quality( quality ); + //TODO!: move some logic up to item type + return item::spawn_temporary( i.fake_item )->get_quality( quality ); } bool bionic::is_this_fuel_powered( const itype_id &this_fuel ) const diff --git a/src/bionics_ui.cpp b/src/bionics_ui.cpp index c36d7a8e41de..6499dd4dab1a 100644 --- a/src/bionics_ui.cpp +++ b/src/bionics_ui.cpp @@ -195,7 +195,8 @@ static void draw_bionics_titlebar( const catacurses::window &window, player *p, for( const bionic &bio : *p->my_bionics ) { for( const itype_id &fuel : p->get_fuel_available( bio.id ) ) { found_fuel = true; - const item temp_fuel( fuel ); + //TODO!: figure out tname so we don't need this + const item &temp_fuel = *item::spawn_temporary( fuel ); if( temp_fuel.has_flag( flag_PERPETUAL ) ) { if( fuel == itype_id( "sunlight" ) && !g->is_in_sunlight( p->pos() ) ) { continue; @@ -209,7 +210,7 @@ static void draw_bionics_titlebar( const catacurses::window &window, player *p, if( bio.info().is_remote_fueled && p->has_active_bionic( bio.id ) ) { const itype_id rem_fuel = p->find_remote_fuel( true ); if( !rem_fuel.is_empty() ) { - const item tmp_rem_fuel( rem_fuel ); + const item &tmp_rem_fuel = *item::spawn_temporary( rem_fuel ); if( tmp_rem_fuel.has_flag( flag_PERPETUAL ) ) { fuel_string += colorize( tmp_rem_fuel.tname(), c_green ) + " "; } else { diff --git a/src/cata_arena.h b/src/cata_arena.h new file mode 100644 index 000000000000..f4b416bf6336 --- /dev/null +++ b/src/cata_arena.h @@ -0,0 +1,49 @@ +#pragma once +#ifndef CATA_SRC_ARENA_H +#define CATA_SRC_ARENA_H + +#include + +template +class cata_arena +{ + private: + static std::vector pending_deletion; + + //TODO!: enable this stuff in debug mode only + static std::vector full_list; + + public: + using value_type = T; + + T *allocate( size_t n ) { + return static_cast( malloc( sizeof( T ) * n ) ); + } + + /* void deallocate( T *alloc ) { + pending_deletion.push_back( alloc ); + }*/ + + static void mark_for_destruction( T *alloc ) { + pending_deletion.push_back( alloc ); + } + + static void check_for_leaks() { + for( T *&obj : full_list ) { + obj->check_location(); + } + } + + static void cleanup() { + for( T *&p : pending_deletion ) { + std::remove( full_list.begin(), full_list.end(), p ); + delete p; + } + pending_deletion.clear(); + check_for_leaks(); + } + +}; + + +#endif diff --git a/src/cata_tiles.cpp b/src/cata_tiles.cpp index 3503e5c7faa1..64ea72cce137 100644 --- a/src/cata_tiles.cpp +++ b/src/cata_tiles.cpp @@ -2009,14 +2009,14 @@ bool cata_tiles::draw_from_id_string( const std::string &id, TILE_CATEGORY categ col = t.color; } } else if( category == C_ITEM ) { - item tmp; + item *tmp; if( string_starts_with( found_id, "corpse_" ) ) { - tmp = item( itype_corpse, calendar::start_of_cataclysm ); + tmp = item::spawn_temporary( itype_corpse, calendar::start_of_cataclysm ); } else { - tmp = item( found_id, calendar::start_of_cataclysm ); + tmp = item::spawn_temporary( found_id, calendar::start_of_cataclysm ); } - sym = tmp.symbol().empty() ? ' ' : tmp.symbol().front(); - col = tmp.color(); + sym = tmp->symbol().empty() ? ' ' : tmp->symbol().front(); + col = tmp->color(); } else if( category == C_OVERMAP_TERRAIN ) { const oter_type_str_id tmp( id ); if( tmp.is_valid() ) { @@ -3164,7 +3164,7 @@ bool cata_tiles::draw_zombie_revival_indicators( const tripoint &pos, const lit_ if( tileset_ptr->find_tile_type( ZOMBIE_REVIVAL_INDICATOR ) && !invisible[0] && item_override.find( pos ) == item_override.end() && here.could_see_items( pos, g->u ) ) { for( auto &i : here.i_at( pos ) ) { - if( i.can_revive() ) { + if( i->can_revive() ) { return draw_from_id_string( ZOMBIE_REVIVAL_INDICATOR, C_NONE, empty_string, pos, 0, 0, lit_level::LIT, false, z_drop ); } diff --git a/src/character.cpp b/src/character.cpp index dc35c954d7c6..ac543d5992a9 100644 --- a/src/character.cpp +++ b/src/character.cpp @@ -928,7 +928,7 @@ int Character::swim_speed() const ret += ( 80 - get_skill_level( skill_swimming ) * 3 ) * ( encumb( bp_torso ) / 10 ); if( get_skill_level( skill_swimming ) < 10 ) { for( auto &i : worn ) { - ret += i.volume() / 125_ml * ( 10 - get_skill_level( skill_swimming ) ); + ret += i->volume() / 125_ml * ( 10 - get_skill_level( skill_swimming ) ); } } /** @EFFECT_STR increases swim speed */ @@ -1042,9 +1042,9 @@ void Character::mount_creature( monster &z ) z.add_effect( effect_ridden, 1_turns, num_bp ); if( z.has_effect( effect_tied ) ) { z.remove_effect( effect_tied ); - if( z.tied_item ) { - i_add( *z.tied_item ); - z.tied_item.reset(); + if( z.get_tied_item() ) { + i_add( *z.get_tied_item() ); + z.set_tied_item(nullptr); } } z.mounted_player_id = getID(); @@ -1071,7 +1071,7 @@ void Character::mount_creature( monster &z ) add_msg_if_player( m_good, _( "You climb on the %s." ), z.get_name() ); if( z.has_flag( MF_RIDEABLE_MECH ) ) { if( !z.type->mech_weapon.is_empty() ) { - item mechwep = item( z.type->mech_weapon ); + item &mechwep = *item::spawn( z.type->mech_weapon ); wield( mechwep ); } add_msg_if_player( m_good, _( "You hear your %s whir to life." ), z.get_name() ); @@ -1153,7 +1153,7 @@ void Character::forced_dismount() auto mon = mounted_creature.get(); if( mon->has_flag( MF_RIDEABLE_MECH ) && !mon->type->mech_weapon.is_empty() ) { mech = true; - remove_item( weapon ); + remove_item( *weapon ); } mon->mounted_player_id = character_id(); mon->remove_effect( effect_ridden ); @@ -1259,8 +1259,8 @@ void Character::dismount() monster *critter = mounted_creature.get(); critter->mounted_player_id = character_id(); if( critter->has_flag( MF_RIDEABLE_MECH ) && !critter->type->mech_weapon.is_empty() && - weapon.typeId() == critter->type->mech_weapon ) { - remove_item( weapon ); + weapon->typeId() == critter->type->mech_weapon ) { + remove_item( *weapon ); } if( is_avatar() && g->u.get_grab_type() != OBJECT_NONE ) { add_msg( m_warning, _( "You let go of the grabbed object." ) ); @@ -1842,10 +1842,10 @@ void Character::check_item_encumbrance_flag() { bool update_required = check_encumbrance; for( auto &i : worn ) { - if( !update_required && i.encumbrance_update_ ) { + if( !update_required && i->encumbrance_update_ ) { update_required = true; } - i.encumbrance_update_ = false; + i->encumbrance_update_ = false; } if( update_required ) { @@ -1855,10 +1855,10 @@ void Character::check_item_encumbrance_flag() bool Character::natural_attack_restricted_on( const bodypart_id &bp ) const { - for( const item &i : worn ) { - if( i.covers( bp->token ) && !i.has_flag( "ALLOWS_NATURAL_ATTACKS" ) && - !i.has_flag( "SEMITANGIBLE" ) && - !i.has_flag( "PERSONAL" ) && !i.has_flag( "AURA" ) ) { + for( const item * const &i : worn ) { + if( i->covers( bp->token ) && !i->has_flag( "ALLOWS_NATURAL_ATTACKS" ) && + !i->has_flag( "SEMITANGIBLE" ) && + !i->has_flag( "PERSONAL" ) && !i->has_flag( "AURA" ) ) { return true; } } @@ -2077,8 +2077,7 @@ std::vector Character::get_fuel_available( const bionic_id &bio ) cons { std::vector stored_fuels; for( const itype_id &fuel : bio->fuel_opts ) { - const item tmp_fuel( fuel ); - if( !get_value( fuel.str() ).empty() || tmp_fuel.has_flag( flag_PERPETUAL ) ) { + if( !get_value( fuel.str() ).empty() || fuel->has_flag( flag_PERPETUAL ) ) { stored_fuels.emplace_back( fuel ); } } @@ -2121,7 +2120,7 @@ int Character::get_total_fuel_capacity( const itype_id &fuel ) const void Character::update_fuel_storage( const itype_id &fuel ) { - const item it( fuel ); + const item &it = *item::spawn_temporary( fuel ); if( get_value( fuel.str() ).empty() ) { for( const bionic_id &bid : get_bionic_fueled_with( it ) ) { remove_value( bid.c_str() ); @@ -2178,7 +2177,7 @@ int Character::get_mod_stat_from_bionic( const character_stat &Stat ) const return ret; } -cata::optional::iterator> Character::wear_item( const item &to_wear, +cata::optional Character::wear_item( item &to_wear, bool interactive ) { const auto ret = can_wear( to_wear ); @@ -2193,8 +2192,8 @@ cata::optional::iterator> Character::wear_item( const item &to_w const bool supertinymouse = get_size() == MS_TINY; last_item = to_wear.typeId(); - std::list::iterator position = position_to_wear_new_item( to_wear ); - std::list::iterator new_item_it = worn.insert( position, to_wear ); + ItemList::iterator position = position_to_wear_new_item( to_wear ); + ItemList::iterator new_item_it = worn.insert( position, &to_wear ); if( interactive ) { add_msg_player_or_npc( @@ -2227,10 +2226,10 @@ cata::optional::iterator> Character::wear_item( const item &to_w add_msg_if_npc( _( " puts on their %s." ), to_wear.tname() ); } - new_item_it->on_wear( *this ); + ( *new_item_it )->on_wear( *this ); - inv.update_invlet( *new_item_it ); - inv.update_cache_with_item( *new_item_it ); + inv.update_invlet( **new_item_it ); + inv.update_cache_with_item( **new_item_it ); recalc_sight_limits(); reset_encumbrance(); @@ -2242,38 +2241,38 @@ int Character::amount_worn( const itype_id &id ) const { int amount = 0; for( auto &elem : worn ) { - if( elem.typeId() == id ) { + if( elem->typeId() == id ) { ++amount; } } return amount; } -std::vector Character::nearby( const +std::vector Character::nearby( const std::function &func, int radius ) const { - std::vector res; + std::vector res; - visit_items( [&]( const item * e, const item * parent ) { + visit_items( [&]( item * e, item * parent ) { if( func( e, parent ) ) { - res.emplace_back( const_cast( *this ), const_cast( e ) ); + res.emplace_back( e ); } return VisitResponse::NEXT; } ); for( const auto &cur : map_selector( pos(), radius ) ) { - cur.visit_items( [&]( const item * e, const item * parent ) { + cur.visit_items( [&]( item * e, item * parent ) { if( func( e, parent ) ) { - res.emplace_back( cur, const_cast( e ) ); + res.emplace_back( e ); } return VisitResponse::NEXT; } ); } for( const auto &cur : vehicle_selector( pos(), radius ) ) { - cur.visit_items( [&]( const item * e, const item * parent ) { + cur.visit_items( [&]( item * e, item * parent ) { if( func( e, parent ) ) { - res.emplace_back( cur, const_cast( e ) ); + res.emplace_back( e ); } return VisitResponse::NEXT; } ); @@ -2318,7 +2317,7 @@ int Character::i_add_to_container( const item &it, const bool unloading ) return charges; } -item &Character::i_add( item it, bool should_stack ) +item &Character::i_add( item &it, bool should_stack ) { itype_id item_type_id = it.typeId(); last_item = item_type_id; @@ -2344,16 +2343,15 @@ item &Character::i_add( item it, bool should_stack ) return item_in_inv; } -std::list Character::remove_worn_items_with( std::function filter ) +ItemList Character::remove_worn_items_with( std::function filter ) { - std::list result; + ItemList result; for( auto iter = worn.begin(); iter != worn.end(); ) { - if( filter( *iter ) ) { - iter->on_takeoff( *this ); - result.splice( result.begin(), worn, iter++ ); - } else { - ++iter; + if( filter( **iter ) ) { + ( *iter )->on_takeoff( *this ); + result.push_back( *iter ); } + ++iter; } return result; } @@ -2385,14 +2383,14 @@ item *Character::invlet_to_item( const int linvlet ) const item &Character::i_at( int position ) const { if( position == -1 ) { - return weapon; + return *weapon; } if( position < -1 ) { int worn_index = worn_position_to_index( position ); if( static_cast( worn_index ) < worn.size() ) { auto iter = worn.begin(); std::advance( iter, worn_index ); - return *iter; + return **iter; } } @@ -2406,13 +2404,13 @@ item &Character::i_at( int position ) int Character::get_item_position( const item *it ) const { - if( weapon.has_item( *it ) ) { + if( weapon->has_item( *it ) ) { return -1; } int p = 0; for( const auto &e : worn ) { - if( e.has_item( *it ) ) { + if( e->has_item( *it ) ) { return worn_position_to_index( p ); } p++; @@ -2420,35 +2418,34 @@ int Character::get_item_position( const item *it ) const return inv.position_by_item( it ); } - -item Character::i_rem( int pos ) +//TODO!: check these +item &Character::i_rem( int pos ) { - item tmp; + item *tmp; if( pos == -1 ) { tmp = weapon; - weapon = item(); - return tmp; + weapon = nullptr; + return *tmp; } else if( pos < -1 && pos > worn_position_to_index( worn.size() ) ) { auto iter = worn.begin(); std::advance( iter, worn_position_to_index( pos ) ); tmp = *iter; - tmp.on_takeoff( *this ); + tmp->on_takeoff( *this ); worn.erase( iter ); - return tmp; + return *tmp; } return inv.remove_item( pos ); } - -item Character::i_rem( const item *it ) +//TODO!: check these +item &Character::i_rem( const item *it ) { auto tmp = remove_items_with( [&it]( const item & i ) { return &i == it; }, 1 ); if( tmp.empty() ) { debugmsg( "did not found item %s to remove it!", it->tname() ); - return item(); } - return tmp.front(); + return *tmp.front(); } void Character::i_rem_keep_contents( const int idx ) @@ -2480,17 +2477,17 @@ std::list Character::get_dependent_worn_items( const item &it ) const std::list dependent; // Adds dependent worn items recursively const std::function add_dependent = [&]( const item & it ) { - for( const item &wit : worn ) { - if( &wit == &it || !wit.is_worn_only_with( it ) ) { + for( const item * const &wit : worn ) { + if( wit == &it || !wit->is_worn_only_with( it ) ) { continue; } const auto iter = std::find_if( dependent.begin(), dependent.end(), [&wit]( const item * dit ) { - return &wit == dit; + return wit == dit; } ); if( iter == dependent.end() ) { // Not in the list yet - add_dependent( wit ); - dependent.push_back( const_cast( & wit ) ); + add_dependent( *wit ); + dependent.push_back( const_cast( wit ) ); } } }; @@ -2502,18 +2499,18 @@ std::list Character::get_dependent_worn_items( const item &it ) const return dependent; } -void Character::drop( item_location loc, const tripoint &where ) +void Character::drop( item& loc, const tripoint &where ) { - item &oThisItem = *loc; + item &oThisItem = loc; if( is_wielding( oThisItem ) ) { - const auto ret = can_unwield( *loc ); + const auto ret = can_unwield( loc ); if( !ret.success() ) { add_msg( m_info, "%s", ret.c_str() ); return; } } else if( is_wearing( oThisItem ) ) { - const auto ret = as_player()->can_takeoff( *loc ); + const auto ret = as_player()->can_takeoff( loc ); if( !ret.success() ) { add_msg( m_info, "%s", ret.c_str() ); @@ -2521,7 +2518,7 @@ void Character::drop( item_location loc, const tripoint &where ) } } - drop( { drop_location( loc, loc->count() ) }, where ); + drop( { drop_location( loc, loc.count() ) }, where ); } void Character::drop( const drop_locations &what, const tripoint &target, @@ -2548,9 +2545,9 @@ invlets_bitset Character::allocated_invlets() const { invlets_bitset invlets = inv.allocated_invlets(); - invlets.set( weapon.invlet ); + invlets.set( weapon->invlet ); for( const auto &w : worn ) { - invlets.set( w.invlet ); + invlets.set( w->invlet ); } invlets[0] = false; @@ -2565,12 +2562,13 @@ bool Character::has_active_item( const itype_id &id ) const } ); } -item Character::remove_weapon() +item &Character::remove_weapon() { - item tmp = weapon; - weapon = item(); + item *tmp = weapon; + weapon = &null_item_reference(); cached_info.erase( "weapon_value" ); - return tmp; + tmp->remove_owner(); + return *tmp; } void Character::remove_mission_items( int mission_id ) @@ -2581,7 +2579,7 @@ void Character::remove_mission_items( int mission_id ) remove_items_with( has_mission_item_filter { mission_id } ); } -std::vector Character::get_ammo( const ammotype &at ) const +std::vector Character::get_ammo( const ammotype &at ) const { return items_with( [at]( const item & it ) { return it.ammo_type() == at; @@ -2596,7 +2594,7 @@ void find_ammo_helper( T &src, const item &obj, bool empty, Output out, bool nes auto contents_id = obj.contents.front().typeId(); // Look for containers with the same type of liquid as that already in our container - src.visit_items( [&src, &nested, &out, &contents_id, &obj]( item * node ) { + src.visit_items( [&nested, &out, &contents_id, &obj]( item * node ) { if( node == &obj ) { // This stops containers and magazines counting *themselves* as ammo sources. return VisitResponse::SKIP; @@ -2604,15 +2602,15 @@ void find_ammo_helper( T &src, const item &obj, bool empty, Output out, bool nes if( node->is_container() && !node->is_container_empty() && node->contents.front().typeId() == contents_id ) { - out = item_location( src, node ); + out = node; } return nested ? VisitResponse::NEXT : VisitResponse::SKIP; } ); } else { // Look for containers with any liquid - src.visit_items( [&src, &nested, &out]( item * node ) { + src.visit_items( [&nested, &out]( item * node ) { if( node->is_container() && node->contents_made_of( LIQUID ) ) { - out = item_location( src, node ); + out = node; } return nested ? VisitResponse::NEXT : VisitResponse::SKIP; } ); @@ -2623,7 +2621,7 @@ void find_ammo_helper( T &src, const item &obj, bool empty, Output out, bool nes std::set ammo = obj.ammo_types(); const auto mags = obj.magazine_compatible(); - src.visit_items( [&src, &nested, &out, &mags, ammo]( item * node ) { + src.visit_items( [&nested, &out, &mags, ammo]( item * node ) { if( node->is_gun() || node->is_tool() ) { // guns/tools never contain usable ammo so most efficient to skip them now return VisitResponse::SKIP; @@ -2636,7 +2634,7 @@ void find_ammo_helper( T &src, const item &obj, bool empty, Output out, bool nes !node->contents_made_of( SOLID ) ) { for( const ammotype &at : ammo ) { if( node->contents.front().ammo_type() == at ) { - out = item_location( src, node ); + out = node; } } return VisitResponse::SKIP; @@ -2644,12 +2642,12 @@ void find_ammo_helper( T &src, const item &obj, bool empty, Output out, bool nes for( const ammotype &at : ammo ) { if( node->ammo_type() == at ) { - out = item_location( src, node ); + out = node; } } if( node->is_magazine() && node->has_flag( flag_SPEEDLOADER ) ) { if( mags.count( node->typeId() ) && node->ammo_remaining() ) { - out = item_location( src, node ); + out = node; } } return nested ? VisitResponse::NEXT : VisitResponse::SKIP; @@ -2658,13 +2656,13 @@ void find_ammo_helper( T &src, const item &obj, bool empty, Output out, bool nes // find compatible magazines excluding those already loaded in tools/guns const auto mags = obj.magazine_compatible(); - src.visit_items( [&src, &nested, &out, mags, empty]( item * node ) { + src.visit_items( [&nested, &out, mags, empty]( item * node ) { if( node->is_gun() || node->is_tool() ) { return VisitResponse::SKIP; } if( node->is_magazine() ) { if( mags.count( node->typeId() ) && ( node->ammo_remaining() || empty ) ) { - out = item_location( src, node ); + out = node; } return VisitResponse::SKIP; } @@ -2673,9 +2671,9 @@ void find_ammo_helper( T &src, const item &obj, bool empty, Output out, bool nes } } -std::vector Character::find_ammo( const item &obj, bool empty, int radius ) const +std::vector Character::find_ammo( const item &obj, bool empty, int radius ) const { - std::vector res; + std::vector res; find_ammo_helper( const_cast( *this ), obj, empty, std::back_inserter( res ), true ); @@ -2691,11 +2689,11 @@ std::vector Character::find_ammo( const item &obj, bool empty, in return res; } -std::vector Character::find_reloadables() +std::vector Character::find_reloadables() { - std::vector reloadables; + std::vector reloadables; - visit_items( [this, &reloadables]( item * node ) { + visit_items( [&reloadables]( item * node ) { if( node->is_holster() ) { return VisitResponse::NEXT; } @@ -2709,7 +2707,7 @@ std::vector Character::find_reloadables() node->ammo_remaining() < node->ammo_capacity(); } if( reloadable ) { - reloadables.push_back( item_location( *this, node ) ); + reloadables.push_back( node ); } return VisitResponse::SKIP; } ); @@ -2754,8 +2752,8 @@ units::mass Character::weight_carried_reduced_by( const excluded_stacks &without // Worn items units::mass ret = 0_gram; for( auto &i : worn ) { - if( !without.count( &i ) ) { - ret += i.weight(); + if( !without.count( i ) ) { + ret += i->weight(); } } @@ -2764,19 +2762,20 @@ units::mass Character::weight_carried_reduced_by( const excluded_stacks &without // Wielded item units::mass weaponweight = 0_gram; - auto weapon_it = without.find( &weapon ); + auto weapon_it = without.find( weapon ); if( weapon_it == without.end() ) { - weaponweight = weapon.weight(); + weaponweight = weapon->weight(); } else { int subtract_count = ( *weapon_it ).second; - if( weapon.count_by_charges() ) { - item copy = weapon; - copy.charges -= subtract_count; - if( copy.charges < 0 ) { + if( weapon->count_by_charges() ) { + //TODO!: CHECK + item *copy = weapon; + copy->charges -= subtract_count; + if( copy->charges < 0 ) { debugmsg( "Trying to remove more charges than the wielded item has" ); - copy.charges = 0; + copy->charges = 0; } - weaponweight = copy.weight(); + weaponweight = copy->weight(); } else if( subtract_count > 1 ) { debugmsg( "Trying to remove more than one wielded item" ); } @@ -2818,9 +2817,9 @@ units::mass Character::weight_capacity() const ret *= mutation_value( "weight_capacity_modifier" ); units::mass worn_weight_bonus = 0_gram; - for( const item &it : worn ) { - ret *= it.get_weight_capacity_modifier(); - worn_weight_bonus += it.get_weight_capacity_bonus(); + for( const item * const &it : worn ) { + ret *= it->get_weight_capacity_modifier(); + worn_weight_bonus += it->get_weight_capacity_bonus(); } units::mass bio_weight_bonus = 0_gram; @@ -2862,8 +2861,8 @@ units::volume Character::volume_capacity_reduced_by( units::volume ret = -mod; for( const auto &i : worn ) { - if( !without.count( &i ) ) { - ret += i.get_storage(); + if( !without.count( i ) ) { + ret += i->get_storage(); } } if( has_bionic( bio_storage ) ) { @@ -2887,7 +2886,7 @@ units::volume Character::volume_capacity_reduced_by( bool Character::can_pick_volume( const item &it ) const { inventory projected = inv; - projected.add_item( it, true ); + projected.add_item( const_cast( it ), true ); return projected.volume() <= volume_capacity(); } @@ -2914,12 +2913,12 @@ bool Character::can_pick_weight( units::mass weight, bool safe ) const } } -bool Character::can_use( const item &it, const item &context ) const +bool Character::can_use( const item &it, const item *context ) const { - const auto &ctx = !context.is_null() ? context : it; + const auto &ctx = context ? *context : it; - if( !meets_requirements( it, ctx ) ) { - const std::string unmet( enumerate_unmet_requirements( it, ctx ) ); + if( !meets_requirements( it, &ctx ) ) { + const std::string unmet( enumerate_unmet_requirements( it, &ctx ) ); if( &it == &ctx ) { //~ %1$s - list of unmet requirements, %2$s - item name. @@ -3005,10 +3004,10 @@ ret_val Character::can_wear( const item &it, bool with_equip_change ) cons if( it.is_power_armor() ) { for( auto &elem : worn ) { - if( ( elem.get_covered_body_parts() & it.get_covered_body_parts() ).any() && - !elem.has_flag( flag_POWERARMOR_COMPATIBLE ) && !elem.is_power_armor() ) { + if( ( elem->get_covered_body_parts() & it.get_covered_body_parts() ).any() && + !elem->has_flag( flag_POWERARMOR_COMPATIBLE ) && !elem->is_power_armor() ) { return ret_val::make_failure( _( "Can't wear power armor over other gear!" ) ); - } else if( elem.has_flag( flag_POWERARMOR_EXO ) && it.has_flag( flag_POWERARMOR_EXO ) ) { + } else if( elem->has_flag( flag_POWERARMOR_EXO ) && it.has_flag( flag_POWERARMOR_EXO ) ) { return ret_val::make_failure( _( "Can't wear multiple exoskeletons!" ) ); } } @@ -3018,11 +3017,11 @@ ret_val Character::can_wear( const item &it, bool with_equip_change ) cons } if( it.has_flag( flag_POWERARMOR_EXTERNAL ) ) { for( auto &elem : worn ) { - if( elem.has_flag( flag_POWERARMOR_EXO ) && - ( elem.get_covered_body_parts() & it.get_covered_body_parts() ).any() ) { + if( elem->has_flag( flag_POWERARMOR_EXO ) && + ( elem->get_covered_body_parts() & it.get_covered_body_parts() ).any() ) { return ret_val::make_failure( _( "Can't wear externals over an exoskeleton!" ) ); - } else if( elem.has_flag( flag_POWERARMOR_EXTERNAL ) && - ( elem.get_covered_body_parts() & it.get_covered_body_parts() ).any() ) { + } else if( elem->has_flag( flag_POWERARMOR_EXTERNAL ) && + ( elem->get_covered_body_parts() & it.get_covered_body_parts() ).any() ) { return ret_val::make_failure( _( "Can't wear externals over one another!" ) ); } } @@ -3045,9 +3044,9 @@ ret_val Character::can_wear( const item &it, bool with_equip_change ) cons for( auto &elem : worn ) { // To check if there's an external/exoskeleton for the mod to attach to. for( std::pair< body_part, bool > &attachment : attachments ) { - if( elem.get_covered_body_parts().test( attachment.first ) && - ( elem.has_flag( flag_POWERARMOR_EXO ) || elem.has_flag( flag_POWERARMOR_EXTERNAL ) ) ) { - if( elem.is_sided() && elem.get_side() == bpid->part_side ) { + if( elem->get_covered_body_parts().test( attachment.first ) && + ( elem->has_flag( flag_POWERARMOR_EXO ) || elem->has_flag( flag_POWERARMOR_EXTERNAL ) ) ) { + if( elem->is_sided() && elem->get_side() == bpid->part_side ) { attachment.second = true; } else { attachment.second = true; @@ -3057,9 +3056,9 @@ ret_val Character::can_wear( const item &it, bool with_equip_change ) cons // To check how many mods are on a given part. for( std::pair< body_part, int > &mod_part : mod_parts ) { bpid = convert_bp( mod_part.first ); - if( elem.get_covered_body_parts().test( mod_part.first ) && - elem.has_flag( flag_POWERARMOR_MOD ) ) { - if( elem.is_sided() && elem.get_side() == bpid->part_side ) { + if( elem->get_covered_body_parts().test( mod_part.first ) && + elem->has_flag( flag_POWERARMOR_MOD ) ) { + if( elem->is_sided() && elem->get_side() == bpid->part_side ) { mod_part.second++; } else { mod_part.second++; @@ -3098,7 +3097,7 @@ ret_val Character::can_wear( const item &it, bool with_equip_change ) cons // You can't wear headgear if power armor helmet is already sitting on your head. for( auto &elem : worn ) { if( !it.has_flag( flag_POWERARMOR_COMPATIBLE ) && ( is_wearing_power_armor() && - ( elem.get_covered_body_parts() & it.get_covered_body_parts() ).any() ) ) { + ( elem->get_covered_body_parts() & it.get_covered_body_parts() ).any() ) ) { return ret_val::make_failure( _( "Can't wear %s with power armor!" ), it.tname() ); } } @@ -3106,13 +3105,13 @@ ret_val Character::can_wear( const item &it, bool with_equip_change ) cons // Check if we don't have both hands available before wearing a briefcase, shield, etc. Also occurs if we're already wearing one. if( it.has_flag( flag_RESTRICT_HANDS ) && ( worn_with_flag( flag_RESTRICT_HANDS ) || - weapon.is_two_handed( *this ) ) ) { + weapon->is_two_handed( *this ) ) ) { return ret_val::make_failure( ( is_player() ? _( "You don't have a hand free to wear that." ) : string_format( _( "%s doesn't have a hand free to wear that." ), name ) ) ); } for( auto &i : worn ) { - if( i.has_flag( flag_ONLY_ONE ) && i.typeId() == it.typeId() ) { + if( i->has_flag( flag_ONLY_ONE ) && i->typeId() == it.typeId() ) { return ret_val::make_failure( _( "Can't wear more than one %s!" ), it.tname() ); } } @@ -3178,8 +3177,8 @@ ret_val Character::can_swap( const item &it ) const for( auto &elem : worn ) { for( std::pair< body_part, int > &mod_part : mod_parts ) { bpid = convert_bp( mod_part.first ); - if( elem.get_covered_body_parts().test( bpid->opposite_part->token ) && - elem.has_flag( flag_POWERARMOR_MOD ) ) { + if( elem->get_covered_body_parts().test( bpid->opposite_part->token ) && + elem->has_flag( flag_POWERARMOR_MOD ) ) { mod_part.second++; } } @@ -3198,13 +3197,13 @@ ret_val Character::can_swap( const item &it ) const void Character::drop_invalid_inventory() { bool dropped_liquid = false; - for( const std::list *stack : inv.const_slice() ) { - const item &it = stack->front(); - if( it.made_of( LIQUID ) ) { + for( ItemList *stack : inv.slice() ) { + item *&it = stack->front(); + if( it->made_of( LIQUID ) ) { dropped_liquid = true; - get_map().add_item_or_charges( pos(), it ); + get_map().add_item_or_charges( pos(), *it ); // must be last - i_rem( &it ); + i_rem( it ); } } if( dropped_liquid ) { @@ -3219,11 +3218,11 @@ void Character::drop_invalid_inventory() bool Character::has_artifact_with( const art_effect_passive effect ) const { - if( weapon.has_effect_when_wielded( effect ) ) { + if( weapon->has_effect_when_wielded( effect ) ) { return true; } for( auto &i : worn ) { - if( i.has_effect_when_worn( effect ) ) { + if( i->has_effect_when_worn( effect ) ) { return true; } } @@ -3234,13 +3233,13 @@ bool Character::has_artifact_with( const art_effect_passive effect ) const bool Character::is_wielding( const item &target ) const { - return &weapon == ⌖ + return weapon == ⌖ } bool Character::is_wearing( const item &itm ) const { for( auto &i : worn ) { - if( &i == &itm ) { + if( i == &itm ) { return true; } } @@ -3250,7 +3249,7 @@ bool Character::is_wearing( const item &itm ) const bool Character::is_wearing( const itype_id &it ) const { for( auto &i : worn ) { - if( i.typeId() == it ) { + if( i->typeId() == it ) { return true; } } @@ -3260,7 +3259,7 @@ bool Character::is_wearing( const itype_id &it ) const bool Character::is_wearing_on_bp( const itype_id &it, const bodypart_id &bp ) const { for( auto &i : worn ) { - if( i.typeId() == it && i.covers( bp->token ) ) { + if( i->typeId() == it && i->covers( bp->token ) ) { return true; } } @@ -3269,19 +3268,19 @@ bool Character::is_wearing_on_bp( const itype_id &it, const bodypart_id &bp ) co bool Character::worn_with_flag( const std::string &flag, const bodypart_id &bp ) const { - return std::any_of( worn.begin(), worn.end(), [&flag, bp]( const item & it ) { - return it.has_flag( flag ) && ( bp == bodypart_str_id::NULL_ID() || - it.covers( bp->token ) ); + return std::any_of( worn.begin(), worn.end(), [&flag, bp]( const item * const & it ) { + return it->has_flag( flag ) && ( bp == bodypart_str_id::NULL_ID() || + it->covers( bp->token ) ); } ); } const item *Character::item_worn_with_flag( const std::string &flag, const bodypart_id &bp ) const { const item *it_with_flag = nullptr; - for( const item &it : worn ) { - if( it.has_flag( flag ) && ( bp == bodypart_str_id::NULL_ID() || - it.covers( bp->token ) ) ) { - it_with_flag = ⁢ + for( const item * const &it : worn ) { + if( it->has_flag( flag ) && ( bp == bodypart_str_id::NULL_ID() || + it->covers( bp->token ) ) ) { + it_with_flag = it; break; } } @@ -3322,14 +3321,14 @@ std::vector Character::get_overlay_ids() const // next clothing // TODO: worry about correct order of clothing overlays - for( const item &worn_item : worn ) { - rval.push_back( "worn_" + worn_item.typeId().str() ); + for( const item * const &worn_item : worn ) { + rval.push_back( "worn_" + worn_item->typeId().str() ); } // last weapon // TODO: might there be clothing that covers the weapon? if( is_armed() ) { - rval.push_back( "wielded_" + weapon.typeId().str() ); + rval.push_back( "wielded_" + weapon->typeId().str() ); } if( move_mode != CMM_WALK ) { @@ -3373,7 +3372,7 @@ void Character::mod_skill_level( const skill_id &ident, const int delta ) _skills->mod_skill_level( ident, delta ); } -std::string Character::enumerate_unmet_requirements( const item &it, const item &context ) const +std::string Character::enumerate_unmet_requirements( const item &it, const item *context ) const { std::vector unmet_reqs; @@ -3389,8 +3388,8 @@ std::string Character::enumerate_unmet_requirements( const item &it, const item check_req( _( "perception" ), get_per(), it.type->min_per ); for( const auto &elem : it.type->min_skills ) { - check_req( context.contextualize_skill( elem.first )->name(), - get_skill_level( elem.first, context ), + check_req( context->contextualize_skill( elem.first )->name(), + get_skill_level( elem.first, *context ), elem.second ); } @@ -3440,9 +3439,9 @@ int Character::read_speed( bool return_stat_effect ) const } bool Character::meets_skill_requirements( const std::map &req, - const item &context ) const + const item *context ) const { - return _skills->meets_skill_requirements( req, context ); + return _skills->meets_skill_requirements( req, *context ); } bool Character::meets_skill_requirements( const construction &con ) const @@ -3461,10 +3460,10 @@ bool Character::meets_stat_requirements( const item &it ) const get_per() >= it.type->min_per; } -bool Character::meets_requirements( const item &it, const item &context ) const +bool Character::meets_requirements( const item &it, const item *context ) const { - const auto &ctx = !context.is_null() ? context : it; - return meets_stat_requirements( it ) && meets_skill_requirements( it.type->min_skills, ctx ); + const auto &ctx = context ? *context : it; + return meets_stat_requirements( it ) && meets_skill_requirements( it.type->min_skills, &ctx ); } void Character::normalize() @@ -3472,7 +3471,8 @@ void Character::normalize() Creature::normalize(); martial_arts_data->reset_style(); - weapon = item( "null", calendar::start_of_cataclysm ); + //TODO!: destroy current weapon + weapon = nullptr;//item( "null", calendar::start_of_cataclysm ); set_body(); recalc_hp(); @@ -3484,16 +3484,17 @@ void Character::die( Creature *nkiller ) g->set_critter_died(); set_killer( nkiller ); set_time_died( calendar::turn ); + //TODO!: restore these if( has_effect( effect_lightsnare ) ) { - inv.add_item( item( itype_string_36, calendar::start_of_cataclysm ) ); - inv.add_item( item( itype_snare_trigger, calendar::start_of_cataclysm ) ); + //inv.add_item( item( itype_string_36, calendar::start_of_cataclysm ) ); + //inv.add_item( item( itype_snare_trigger, calendar::start_of_cataclysm ) ); } if( has_effect( effect_heavysnare ) ) { - inv.add_item( item( itype_rope_6, calendar::start_of_cataclysm ) ); - inv.add_item( item( itype_snare_trigger, calendar::start_of_cataclysm ) ); + //inv.add_item( item( itype_rope_6, calendar::start_of_cataclysm ) ); + //inv.add_item( item( itype_snare_trigger, calendar::start_of_cataclysm ) ); } if( has_effect( effect_beartrap ) ) { - inv.add_item( item( itype_beartrap, calendar::start_of_cataclysm ) ); + //inv.add_item( item( itype_beartrap, calendar::start_of_cataclysm ) ); } mission::on_creature_death( *this ); } @@ -3647,7 +3648,8 @@ void Character::reset_encumbrance() char_encumbrance_data Character::calc_encumbrance() const { - return calc_encumbrance( item() ); + //TODO!: what the shit + return calc_encumbrance( null_item_reference() ); } char_encumbrance_data Character::calc_encumbrance( const item &new_item ) const @@ -3665,14 +3667,14 @@ units::mass Character::get_weight() const { units::mass ret = 0_gram; units::mass wornWeight = std::accumulate( worn.begin(), worn.end(), 0_gram, - []( units::mass sum, const item & itm ) { - return sum + itm.weight(); + []( units::mass sum, const item * const & itm ) { + return sum + itm->weight(); } ); ret += bodyweight(); // The base weight of the player's body ret += inv.weight(); // Weight of the stored inventory ret += wornWeight; // Weight of worn items - ret += weapon.weight(); // Weight of wielded item + ret += weapon->weight(); // Weight of wielded item ret += bionics_weight(); // Weight of installed bionics return ret; } @@ -3724,7 +3726,7 @@ bool Character::change_side( item &it, bool interactive ) return true; } -bool Character::change_side( item_location &loc, bool interactive ) +bool Character::change_side( item *loc, bool interactive ) { if( !loc || !is_worn( *loc ) ) { if( interactive ) { @@ -3784,10 +3786,10 @@ bool Character::is_wearing_power_armor( bool *hasHelmet ) const { bool result = false; for( auto &elem : worn ) { - if( !elem.is_power_armor() ) { + if( !elem->is_power_armor() ) { continue; } - if( elem.has_flag( flag_POWERARMOR_EXO ) ) { + if( elem->has_flag( flag_POWERARMOR_EXO ) ) { result = true; if( hasHelmet == nullptr ) { // found power armor, helmet not requested, cancel loop @@ -3795,7 +3797,7 @@ bool Character::is_wearing_power_armor( bool *hasHelmet ) const } } // found power armor, continue search for helmet - if( elem.covers( bp_head ) ) { + if( elem->covers( bp_head ) ) { if( hasHelmet != nullptr ) { *hasHelmet = true; } @@ -3808,7 +3810,7 @@ bool Character::is_wearing_power_armor( bool *hasHelmet ) const bool Character::is_wearing_active_power_armor() const { for( const auto &w : worn ) { - if( w.has_flag( flag_POWERARMOR_EXO ) && w.active ) { + if( w->has_flag( flag_POWERARMOR_EXO ) && w->active ) { return true; } } @@ -3818,7 +3820,7 @@ bool Character::is_wearing_active_power_armor() const bool Character::is_wearing_active_optcloak() const { for( auto &w : worn ) { - if( w.active && w.has_flag( flag_ACTIVE_CLOAKING ) ) { + if( w->active && w->has_flag( flag_ACTIVE_CLOAKING ) ) { return true; } } @@ -3838,7 +3840,7 @@ bool Character::in_climate_control() return true; } for( auto &w : worn ) { - if( w.has_flag( flag_CLIMATE_CONTROL.str() ) ) { + if( w->has_flag( flag_CLIMATE_CONTROL.str() ) ) { return true; } } @@ -3926,14 +3928,14 @@ int layer_details::layer( const int encumbrance ) return total - current; } -std::list::iterator Character::position_to_wear_new_item( const item &new_item ) +ItemList::iterator Character::position_to_wear_new_item( const item &new_item ) { // By default we put this item on after the last item on the same or any // lower layer. return std::find_if( worn.rbegin(), worn.rend(), - [&]( const item & w ) { - return w.get_layer() <= new_item.get_layer(); + [&]( const item * const & w ) { + return w->get_layer() <= new_item.get_layer(); } ).base(); } @@ -3962,7 +3964,7 @@ void Character::item_encumb( char_encumbrance_data &vals, const item &new_item ) vals = char_encumbrance_data(); // Figure out where new_item would be worn - std::list::const_iterator new_item_position = worn.end(); + ItemList::const_iterator new_item_position = worn.end(); if( !new_item.is_null() ) { // const_cast required to work around g++-4.8 library bug // see the commit that added this comment to understand why @@ -3980,7 +3982,7 @@ void Character::item_encumb( char_encumbrance_data &vals, const item &new_item ) if( w_it == new_item_position ) { layer_item( vals, new_item, highest_layer_so_far, *this ); } - layer_item( vals, *w_it, highest_layer_so_far, *this ); + layer_item( vals, **w_it, highest_layer_so_far, *this ); } if( worn.end() == new_item_position && !new_item.is_null() ) { @@ -4046,9 +4048,9 @@ body_part_set Character::exclusive_flag_coverage( const std::string &flag ) cons body_part_set ret = body_part_set::all(); for( const auto &elem : worn ) { - if( !elem.has_flag( flag ) ) { + if( !elem->has_flag( flag ) ) { // Unset the parts covered by this item - ret &= ~elem.get_covered_body_parts(); + ret &= ~elem->get_covered_body_parts(); } } @@ -5271,28 +5273,28 @@ void Character::update_bodytemp( const map &m, const weather_manager &weather ) temp_equalizer( body_part_leg_l, body_part_foot_l ); temp_equalizer( body_part_leg_r, body_part_foot_r ); - for( const item &it : worn ) { + for( const item * const &it : worn ) { // TODO: Port body part set id changes - const body_part_set &covered = it.get_covered_body_parts(); + const body_part_set &covered = it->get_covered_body_parts(); for( size_t i = 0; i < num_bp; i++ ) { body_part token = static_cast( i ); if( covered.test( token ) ) { - clothing_map[convert_bp( token )].emplace_back( &it ); + clothing_map[convert_bp( token )].emplace_back( it ); } - if( it.has_flag( flag_HOOD ) ) { - bonus_clothing_map[body_part_head].emplace_back( &it ); + if( it->has_flag( flag_HOOD ) ) { + bonus_clothing_map[body_part_head].emplace_back( it ); } - if( it.has_flag( flag_COLLAR ) ) { - bonus_clothing_map[body_part_mouth].emplace_back( &it ); + if( it->has_flag( flag_COLLAR ) ) { + bonus_clothing_map[body_part_mouth].emplace_back( it ); } - if( it.has_flag( flag_POCKETS ) ) { - bonus_clothing_map[body_part_hand_l].emplace_back( &it ); - bonus_clothing_map[body_part_hand_r].emplace_back( &it ); + if( it->has_flag( flag_POCKETS ) ) { + bonus_clothing_map[body_part_hand_l].emplace_back( it ); + bonus_clothing_map[body_part_hand_r].emplace_back( it ); } } } // If player is wielding something large, pockets are not usable - if( weapon.volume() >= 500_ml ) { + if( weapon->volume() >= 500_ml ) { bonus_clothing_map[body_part_hand_l].clear(); bonus_clothing_map[body_part_hand_r].clear(); } @@ -5721,11 +5723,11 @@ Character::comfort_response_t Character::base_comfort_value( const tripoint &p ) const cata::optional board = vp.part_with_feature( "BOARDABLE", true ); if( carg ) { const vehicle_stack items = vp->vehicle().get_items( carg->part_index() ); - for( const item &items_it : items ) { - if( items_it.has_flag( "SLEEP_AID" ) ) { + for( const item * const &items_it : items ) { + if( items_it->has_flag( "SLEEP_AID" ) ) { // Note: BED + SLEEP_AID = 9 pts, or 1 pt below very_comfortable comfort += 1 + static_cast( comfort_level::slightly_comfortable ); - comfort_response.aid = &items_it; + comfort_response.aid = items_it; break; // prevents using more than 1 sleep aid } } @@ -5758,11 +5760,11 @@ Character::comfort_response_t Character::base_comfort_value( const tripoint &p ) if( comfort_response.aid == nullptr ) { const map_stack items = here.i_at( p ); - for( const item &items_it : items ) { - if( items_it.has_flag( "SLEEP_AID" ) ) { + for( const item * const &items_it : items ) { + if( items_it->has_flag( "SLEEP_AID" ) ) { // Note: BED + SLEEP_AID = 9 pts, or 1 pt below very_comfortable comfort += 1 + static_cast( comfort_level::slightly_comfortable ); - comfort_response.aid = &items_it; + comfort_response.aid = items_it; break; // prevents using more than 1 sleep aid } } @@ -6270,7 +6272,7 @@ int Character::throw_range( const item &it ) const return -1; } - item tmp = it; + item &tmp = *item::spawn_temporary( it ); if( tmp.count_by_charges() && tmp.charges > 1 ) { tmp.charges = 1; @@ -6306,6 +6308,7 @@ int Character::throw_range( const item &it ) const return str_override * 3 + get_skill_level( skill_throw ); } + return ret; } @@ -6395,11 +6398,11 @@ float Character::active_light() const float curr_lum = 0.0f; for( const auto elem : mut.first->lumination ) { int coverage = 0; - for( const item &i : worn ) { - if( i.covers( elem.first ) && !i.has_flag( flag_ALLOWS_NATURAL_ATTACKS ) && - !i.has_flag( flag_SEMITANGIBLE ) && - !i.has_flag( flag_PERSONAL ) && !i.has_flag( flag_AURA ) ) { - coverage += i.get_coverage(); + for( const item * const &i : worn ) { + if( i->covers( elem.first ) && !i->has_flag( flag_ALLOWS_NATURAL_ATTACKS ) && + !i->has_flag( flag_SEMITANGIBLE ) && + !i->has_flag( flag_PERSONAL ) && !i->has_flag( flag_AURA ) ) { + coverage += i->get_coverage(); } } curr_lum += elem.second * ( 1 - ( coverage / 100.0f ) ); @@ -6466,7 +6469,7 @@ bool Character::pour_into( item &container, item &liquid ) bool Character::pour_into( vehicle &veh, item &liquid ) { auto sel = [&]( const vehicle_part & pt ) { - return pt.is_tank() && pt.can_reload( liquid ); + return pt.is_tank() && pt.can_reload( &liquid ); }; auto stack = units::legacy_volume_factor / liquid.type->stack_size; @@ -6650,16 +6653,16 @@ std::string Character::extended_description() const ss += "--\n"; ss += _( "Wielding:" ) + std::string( " " ); - if( weapon.is_null() ) { + if( weapon->is_null() ) { ss += _( "Nothing" ); } else { - ss += weapon.tname(); + ss += weapon->tname(); } ss += "\n"; ss += _( "Wearing:" ) + std::string( " " ); - ss += enumerate_as_string( worn.begin(), worn.end(), []( const item & it ) { - return it.tname(); + ss += enumerate_as_string( worn.begin(), worn.end(), []( const item * const & it ) { + return it->tname(); } ); return replace_colors( ss ); @@ -7004,8 +7007,8 @@ int Character::get_armor_type( damage_type dt, bodypart_id bp ) const case DT_ELECTRIC: { int ret = 0; for( auto &i : worn ) { - if( i.covers( bp->token ) ) { - ret += i.damage_resist( dt ); + if( i->covers( bp->token ) ) { + ret += i->damage_resist( dt ); } } @@ -7076,8 +7079,8 @@ int Character::get_armor_bash_base( bodypart_id bp ) const { int ret = 0; for( auto &i : worn ) { - if( i.covers( bp->token ) ) { - ret += i.bash_resist(); + if( i->covers( bp->token ) ) { + ret += i->bash_resist(); } } for( const bionic_id &bid : get_bionics() ) { @@ -7095,8 +7098,8 @@ int Character::get_armor_cut_base( bodypart_id bp ) const { int ret = 0; for( auto &i : worn ) { - if( i.covers( bp->token ) ) { - ret += i.cut_resist(); + if( i->covers( bp->token ) ) { + ret += i->cut_resist(); } } for( const bionic_id &bid : get_bionics() ) { @@ -7114,8 +7117,8 @@ int Character::get_armor_bullet_base( bodypart_id bp ) const { int ret = 0; for( auto &i : worn ) { - if( i.covers( bp->token ) ) { - ret += i.bullet_resist(); + if( i->covers( bp->token ) ) { + ret += i->bullet_resist(); } } @@ -7135,8 +7138,8 @@ int Character::get_env_resist( bodypart_id bp ) const int ret = 0; for( auto &i : worn ) { // Head protection works on eyes too (e.g. baseball cap) - if( i.covers( bp->token ) || ( bp == bodypart_id( "eyes" ) && i.covers( bp_head ) ) ) { - ret += i.get_env_resist(); + if( i->covers( bp->token ) || ( bp == bodypart_id( "eyes" ) && i->covers( bp_head ) ) ) { + ret += i->get_env_resist(); } } @@ -7231,7 +7234,7 @@ void Character::burn_move_stamina( int moves ) } int burn_ratio = get_option( "PLAYER_BASE_STAMINA_BURN_RATE" ); - for( const bionic_id &bid : get_bionic_fueled_with( item( "muscle" ) ) ) { + for( const bionic_id &bid : get_bionic_fueled_with( *item::spawn_temporary( "muscle" ) ) ) { if( has_active_bionic( bid ) ) { burn_ratio = burn_ratio * 2 - 3; } @@ -7363,10 +7366,11 @@ bool Character::invoke_item( item *used, const std::string &method, const tripoi return false; } -bool Character::dispose_item( item_location &&obj, const std::string &prompt ) +bool Character::dispose_item( item &obj, const std::string &prompt ) { + //TODO!: check this for location order uilist menu; - menu.text = prompt.empty() ? string_format( _( "Dispose of %s" ), obj->tname() ) : prompt; + menu.text = prompt.empty() ? string_format( _( "Dispose of %s" ), obj.tname() ) : prompt; using dispose_option = struct { std::string prompt; @@ -7378,57 +7382,57 @@ bool Character::dispose_item( item_location &&obj, const std::string &prompt ) std::vector opts; - const bool bucket = obj->is_bucket_nonempty(); + const bool bucket = obj.is_bucket_nonempty(); opts.emplace_back( dispose_option{ bucket ? _( "Spill contents and store in inventory" ) : _( "Store in inventory" ), - volume_carried() + obj->volume() <= volume_capacity(), '1', - item_handling_cost( *obj ), + volume_carried() + obj.volume() <= volume_capacity(), '1', + item_handling_cost( obj ), [this, bucket, &obj] { - if( bucket && !obj->spill_contents( *this ) ) + if( bucket && !obj.spill_contents( *this ) ) { return false; } - moves -= item_handling_cost( *obj ); - inv.add_item_keep_invlet( *obj ); + moves -= item_handling_cost( obj ); + inv.add_item_keep_invlet( obj ); inv.unsort(); - obj.remove_item(); + obj.detach(); return true; } } ); opts.emplace_back( dispose_option{ _( "Drop item" ), true, '2', 0, [this, &obj] { - put_into_vehicle_or_drop( *this, item_drop_reason::deliberate, { *obj } ); - obj.remove_item(); + put_into_vehicle_or_drop( *this, item_drop_reason::deliberate, { &obj } ); + obj.detach(); return true; } } ); opts.emplace_back( dispose_option{ bucket ? _( "Spill contents and wear item" ) : _( "Wear item" ), - can_wear( *obj ).success(), '3', item_wear_cost( *obj ), + can_wear( obj ).success(), '3', item_wear_cost( obj ), [this, bucket, &obj] { - if( bucket && !obj->spill_contents( *this ) ) + if( bucket && !obj.spill_contents( *this ) ) { return false; } - item it = *obj; - obj.remove_item(); + item &it = obj; + obj.detach(); return !!wear_item( it ); } } ); for( auto &e : worn ) { - if( e.can_holster( *obj ) ) { - auto ptr = dynamic_cast( e.type->get_use( "holster" )->get_actor_ptr() ); + if( e->can_holster( obj ) ) { + auto ptr = dynamic_cast( e->type->get_use( "holster" )->get_actor_ptr() ); opts.emplace_back( dispose_option{ - string_format( _( "Store in %s" ), e.tname() ), true, e.invlet, - item_store_cost( *obj, e, false, ptr->draw_cost ), + string_format( _( "Store in %s" ), e->tname() ), true, e->invlet, + item_store_cost( obj, *e, false, ptr->draw_cost ), [this, ptr, &e, &obj] { - return ptr->store( *this->as_player(), e, *obj ); + return ptr->store( *this->as_player(), *e, obj ); } } ); } @@ -7577,7 +7581,7 @@ int Character::item_handling_cost( const item &it, bool penalties, int base_cost mv += std::min( 200, it.volume() / 20_ml ); } - if( weapon.typeId() == itype_e_handcuffs ) { + if( weapon->typeId() == itype_e_handcuffs ) { mv *= 4; } else if( penalties && has_effect( effect_grabbed ) ) { mv *= 2; @@ -8150,7 +8154,7 @@ static void armor_enchantment_adjust( Character &guy, damage_unit &du ) void Character::absorb_hit( const bodypart_id &bp, damage_instance &dam ) { - std::list worn_remains; + ItemList worn_remains; bool armor_destroyed = false; for( damage_unit &elem : dam.damage_units ) { @@ -8221,7 +8225,7 @@ void Character::absorb_hit( const bodypart_id &bp, damage_instance &dam ) // The worn vector has the innermost item first, so // iterate reverse to damage the outermost (last in worn vector) first. for( auto iter = worn.rbegin(); iter != worn.rend(); ) { - item &armor = *iter; + item &armor = **iter; if( !armor.covers( bp->token ) ) { ++iter; @@ -8256,8 +8260,8 @@ void Character::absorb_hit( const bodypart_id &bp, damage_instance &dam ) destroyed_armor_msg( *this, pre_damage_name ); armor_destroyed = true; armor.on_takeoff( *this ); - for( const item *it : armor.contents.all_items_top() ) { - worn_remains.push_back( *it ); + for( item *it : armor.contents.all_items_top() ) { + worn_remains.push_back( it ); } // decltype is the type name of the iterator, note that reverse_iterator::base returns the // iterator to the next element, not the one the revers_iterator points to. @@ -8283,8 +8287,8 @@ void Character::absorb_hit( const bodypart_id &bp, damage_instance &dam ) elem.amount = std::max( elem.amount, 0.0f ); } map &here = get_map(); - for( item &remain : worn_remains ) { - here.add_item_or_charges( pos(), remain ); + for( item *&remain : worn_remains ) { + here.add_item_or_charges( pos(), *remain ); } if( armor_destroyed ) { drop_invalid_inventory(); @@ -8427,12 +8431,12 @@ void Character::apply_damage( Creature *source, bodypart_id hurt, int dam, mod_part_hp_cur( part_to_damage, - dam_to_bodypart ); get_event_bus().send( getID(), dam_to_bodypart ); - if( !weapon.is_null() && !as_player()->can_wield( weapon ).success() && - can_unwield( weapon ).success() ) { + if( !weapon->is_null() && !as_player()->can_wield( *weapon ).success() && + can_unwield( *weapon ).success() ) { add_msg_if_player( _( "You are no longer able to wield your %s and drop it!" ), - weapon.display_name() ); + weapon->display_name() ); put_into_vehicle_or_drop( *this, item_drop_reason::tumbling, { weapon } ); - i_rem( &weapon ); + i_rem( weapon ); } if( has_effect( effect_mending, part_to_damage->token ) ) { effect &e = get_effect( effect_mending, part_to_damage->token ); @@ -8561,7 +8565,7 @@ dealt_damage_instance Character::deal_damage( Creature *source, bodypart_id bp, // TODO: Scale with damage in a way that makes sense for power armors, plate armor and naked skin. - recoil += recoil_mul * weapon.volume() / 250_ml; + recoil += recoil_mul * weapon->volume() / 250_ml; recoil = std::min( MAX_RECOIL, recoil ); //looks like this should be based off of dealt damages, not d as d has no damage reduction applied. // Skip all this if the damage isn't from a creature. e.g. an explosion. @@ -8591,9 +8595,9 @@ dealt_damage_instance Character::deal_damage( Creature *source, bodypart_id bp, if( get_option( "FILTHY_WOUNDS" ) ) { int sum_cover = 0; - for( const item &i : worn ) { - if( i.covers( bp->token ) && i.is_filthy() ) { - sum_cover += i.get_coverage(); + for( const item * const &i : worn ) { + if( i->covers( bp->token ) && i->is_filthy() ) { + sum_cover += i->get_coverage(); } } @@ -8864,7 +8868,7 @@ void Character::rooted() bool Character::wearing_something_on( const bodypart_id &bp ) const { for( auto &i : worn ) { - if( i.covers( bp->token ) ) { + if( i->covers( bp->token ) ) { return true; } } @@ -8877,10 +8881,10 @@ bool Character::is_wearing_shoes( const side &which_side ) const bool right = true; if( which_side == side::LEFT || which_side == side::BOTH ) { left = false; - for( const item &worn_item : worn ) { - if( worn_item.covers( bp_foot_l ) && !worn_item.has_flag( flag_BELTED ) && - !worn_item.has_flag( flag_PERSONAL ) && !worn_item.has_flag( flag_AURA ) && - !worn_item.has_flag( flag_SEMITANGIBLE ) && !worn_item.has_flag( flag_SKINTIGHT ) ) { + for( const item * const &worn_item : worn ) { + if( worn_item->covers( bp_foot_l ) && !worn_item->has_flag( flag_BELTED ) && + !worn_item->has_flag( flag_PERSONAL ) && !worn_item->has_flag( flag_AURA ) && + !worn_item->has_flag( flag_SEMITANGIBLE ) && !worn_item->has_flag( flag_SKINTIGHT ) ) { left = true; break; } @@ -8888,10 +8892,10 @@ bool Character::is_wearing_shoes( const side &which_side ) const } if( which_side == side::RIGHT || which_side == side::BOTH ) { right = false; - for( const item &worn_item : worn ) { - if( worn_item.covers( bp_foot_r ) && !worn_item.has_flag( flag_BELTED ) && - !worn_item.has_flag( flag_PERSONAL ) && !worn_item.has_flag( flag_AURA ) && - !worn_item.has_flag( flag_SEMITANGIBLE ) && !worn_item.has_flag( flag_SKINTIGHT ) ) { + for( const item * const &worn_item : worn ) { + if( worn_item->covers( bp_foot_r ) && !worn_item->has_flag( flag_BELTED ) && + !worn_item->has_flag( flag_PERSONAL ) && !worn_item->has_flag( flag_AURA ) && + !worn_item->has_flag( flag_SEMITANGIBLE ) && !worn_item->has_flag( flag_SKINTIGHT ) ) { right = true; break; } @@ -8902,10 +8906,10 @@ bool Character::is_wearing_shoes( const side &which_side ) const bool Character::is_wearing_helmet() const { - for( const item &i : worn ) { - if( i.covers( bp_head ) && !i.has_flag( flag_HELMET_COMPAT ) && !i.has_flag( flag_SKINTIGHT ) && - !i.has_flag( flag_PERSONAL ) && !i.has_flag( flag_AURA ) && !i.has_flag( flag_SEMITANGIBLE ) && - !i.has_flag( flag_OVERSIZE ) ) { + for( const item * const &i : worn ) { + if( i->covers( bp_head ) && !i->has_flag( flag_HELMET_COMPAT ) && !i->has_flag( flag_SKINTIGHT ) && + !i->has_flag( flag_PERSONAL ) && !i->has_flag( flag_AURA ) && !i->has_flag( flag_SEMITANGIBLE ) && + !i->has_flag( flag_OVERSIZE ) ) { return true; } } @@ -8916,10 +8920,9 @@ int Character::head_cloth_encumbrance() const { int ret = 0; for( auto &i : worn ) { - const item *worn_item = &i; - if( i.covers( bp_head ) && !i.has_flag( flag_SEMITANGIBLE ) && - ( worn_item->has_flag( flag_HELMET_COMPAT ) || worn_item->has_flag( flag_SKINTIGHT ) ) ) { - ret += worn_item->get_encumber( *this ); + if( i->covers( bp_head ) && !i->has_flag( flag_SEMITANGIBLE ) && + ( i->has_flag( flag_HELMET_COMPAT ) || i->has_flag( flag_SKINTIGHT ) ) ) { + ret += i->get_encumber( *this ); } } return ret; @@ -8964,11 +8967,11 @@ int Character::shoe_type_count( const itype_id &it ) const std::vector Character::inv_dump() { std::vector ret; - if( is_armed() && can_unwield( weapon ).success() ) { - ret.push_back( &weapon ); + if( is_armed() && can_unwield( *weapon ).success() ) { + ret.push_back( weapon ); } for( auto &i : worn ) { - ret.push_back( &i ); + ret.push_back( i ); } inv.dump( ret ); return ret; @@ -8983,11 +8986,11 @@ bool Character::covered_with_flag( const std::string &flag, const body_part_set body_part_set to_cover( parts ); for( const auto &elem : worn ) { - if( !elem.has_flag( flag ) ) { + if( !elem->has_flag( flag ) ) { continue; } - to_cover &= ~elem.get_covered_body_parts(); + to_cover &= ~elem->get_covered_body_parts(); if( to_cover.none() ) { return true; // Allows early exit. @@ -9133,8 +9136,8 @@ bool Character::check_and_recover_morale() { player_morale test_morale; - for( const item &wit : worn ) { - test_morale.on_item_wear( wit ); + for( const item * const &wit : worn ) { + test_morale.on_item_wear( *wit ); } for( const trait_id &mut : get_mutations() ) { @@ -9353,12 +9356,12 @@ std::string Character::is_snuggling() const } for( auto candidate = begin; candidate != end; ++candidate ) { - if( !candidate->is_armor() ) { + if( !( *candidate )->is_armor() ) { continue; - } else if( candidate->volume() > 250_ml && candidate->get_warmth() > 0 && - ( candidate->covers( bp_torso ) || candidate->covers( bp_leg_l ) || - candidate->covers( bp_leg_r ) ) ) { - floor_armor = &*candidate; + } else if( ( *candidate )->volume() > 250_ml && ( *candidate )->get_warmth() > 0 && + ( ( *candidate )->covers( bp_torso ) || ( *candidate )->covers( bp_leg_l ) || + ( *candidate )->covers( bp_leg_r ) ) ) { + floor_armor = *candidate; ticker++; } } @@ -9498,16 +9501,16 @@ int Character::floor_item_warmth( const tripoint &pos ) int item_warmth = 0; const auto warm = [&item_warmth]( const auto & stack ) { - for( const item &elem : stack ) { - if( !elem.is_armor() ) { + for( const item * const &elem : stack ) { + if( !elem->is_armor() ) { continue; } // Items that are big enough and covers the torso are used to keep warm. // Smaller items don't do as good a job - if( elem.volume() > 250_ml && - ( elem.covers( bp_torso ) || elem.covers( bp_leg_l ) || - elem.covers( bp_leg_r ) ) ) { - item_warmth += 60 * elem.get_warmth() * elem.volume() / 2500_ml; + if( elem->volume() > 250_ml && + ( elem->covers( bp_torso ) || elem->covers( bp_leg_l ) || + elem->covers( bp_leg_r ) ) ) { + item_warmth += 60 * elem->get_warmth() * elem->volume() / 2500_ml; } } }; @@ -9593,7 +9596,7 @@ bool Character::has_item_with_flag( const std::string &flag, bool need_charges ) } ); } -std::vector Character::all_items_with_flag( const std::string &flag ) const +std::vector Character::all_items_with_flag( const std::string &flag ) const { return items_with( [&flag]( const item & it ) { return it.has_flag( flag ); @@ -9609,7 +9612,8 @@ bool Character::has_charges( const itype_id &it, int quantity, if( it == itype_UPS && is_mounted() && mounted_creature.get()->has_flag( MF_RIDEABLE_MECH ) ) { auto mons = mounted_creature.get(); - return quantity <= mons->battery_item->ammo_remaining(); + //TODO!: null check? + return quantity <= mons->get_battery_item()->ammo_remaining(); } if( it == itype_bio_armor ) { int mod_qty = 0; @@ -9628,16 +9632,16 @@ bool Character::has_charges( const itype_id &it, int quantity, return charges_of( it, quantity, filter ) == quantity; } -std::list Character::use_amount( itype_id it, int quantity, - const std::function &filter ) +ItemList Character::use_amount( itype_id it, int quantity, + const std::function &filter ) { - std::list ret; - if( weapon.use_amount( it, quantity, ret ) ) { + ItemList ret; + if( weapon->use_amount( it, quantity, ret ) ) { remove_weapon(); } for( auto a = worn.begin(); a != worn.end() && quantity > 0; ) { - if( a->use_amount( it, quantity, ret, filter ) ) { - a->on_takeoff( *this ); + if( ( *a )->use_amount( it, quantity, ret, filter ) ) { + ( *a )->on_takeoff( *this ); a = worn.erase( a ); } else { ++a; @@ -9646,8 +9650,9 @@ std::list Character::use_amount( itype_id it, int quantity, if( quantity <= 0 ) { return ret; } - std::list tmp = inv.use_amount( it, quantity, filter ); - ret.splice( ret.end(), tmp ); + ItemList tmp = inv.use_amount( it, quantity, filter ); + ret.insert( ret.end(), tmp.begin(), tmp.end() ); + //ret.splice( ret.end(), tmp ); return ret; } @@ -9660,10 +9665,10 @@ bool Character::use_charges_if_avail( const itype_id &it, int quantity ) return false; } -std::list Character::use_charges( const itype_id &what, int qty, - const std::function &filter ) +ItemList Character::use_charges( const itype_id &what, int qty, + const std::function &filter ) { - std::list res; + ItemList res; if( qty <= 0 ) { return res; @@ -9693,9 +9698,9 @@ std::list Character::use_charges( const itype_id &what, int qty, } else if( what == itype_UPS ) { if( is_mounted() && mounted_creature.get()->has_flag( MF_RIDEABLE_MECH ) && - mounted_creature.get()->battery_item ) { + mounted_creature.get()->get_battery_item() ) { auto mons = mounted_creature.get(); - int power_drain = std::min( mons->battery_item->ammo_remaining(), qty ); + int power_drain = std::min( mons->get_battery_item()->ammo_remaining(), qty ); mons->use_mech_power( -power_drain ); qty -= std::min( qty, power_drain ); return res; @@ -9708,15 +9713,15 @@ std::list Character::use_charges( const itype_id &what, int qty, int adv = charges_of( itype_adv_UPS_off, static_cast( std::ceil( qty * 0.6 ) ) ); if( adv > 0 ) { - std::list found = use_charges( itype_adv_UPS_off, adv ); - res.splice( res.end(), found ); + ItemList found = use_charges( itype_adv_UPS_off, adv ); + res.insert( res.end(), found.begin(), found.end() ); qty -= std::min( qty, static_cast( adv / 0.6 ) ); } int ups = charges_of( itype_UPS_off, qty ); if( ups > 0 ) { - std::list found = use_charges( itype_UPS_off, ups ); - res.splice( res.end(), found ); + ItemList found = use_charges( itype_UPS_off, ups ); + res.insert( res.end(), found.begin(), found.end() ); qty -= std::min( qty, ups ); } return res; @@ -9969,16 +9974,16 @@ const pathfinding_settings &Character::get_pathfinding_settings() const float Character::power_rating() const { - int dmg = std::max( { weapon.damage_melee( DT_BASH ), - weapon.damage_melee( DT_CUT ), - weapon.damage_melee( DT_STAB ) + int dmg = std::max( { weapon->damage_melee( DT_BASH ), + weapon->damage_melee( DT_CUT ), + weapon->damage_melee( DT_STAB ) } ); int ret = 2; // Small guns can be easily hidden from view - if( weapon.volume() <= 250_ml ) { + if( weapon->volume() <= 250_ml ) { ret = 2; - } else if( weapon.is_gun() ) { + } else if( weapon->is_gun() ) { ret = 4; } else if( dmg > 12 ) { ret = 3; // Melee weapon or weapon-y tool @@ -10131,30 +10136,30 @@ void Character::place_corpse() return; } std::vector tmp = inv_dump(); - item body = item::make_corpse( mtype_id::NULL_ID(), calendar::turn, name ); + item &body = item::make_corpse( mtype_id::NULL_ID(), calendar::turn, name ); map &here = get_map(); for( auto itm : tmp ) { here.add_item_or_charges( pos(), *itm ); } for( const bionic &bio : *my_bionics ) { if( bio.info().itype().is_valid() ) { - item cbm( bio.id.str(), calendar::turn ); + item &cbm = *item::spawn( bio.id.str(), calendar::turn ); cbm.faults.emplace( fault_bionic_nonsterile ); - body.components.push_back( cbm ); + body.components.push_back( &cbm ); } } // Restore amount of installed pseudo-modules of Power Storage Units std::pair storage_modules = amount_of_storage_bionics(); for( int i = 0; i < storage_modules.first; ++i ) { - item cbm( itype_power_storage ); + item &cbm = *item::spawn( itype_power_storage ); cbm.faults.emplace( fault_bionic_nonsterile ); - body.components.push_back( cbm ); + body.components.push_back( &cbm ); } for( int i = 0; i < storage_modules.second; ++i ) { - item cbm( itype_power_storage_mkII ); + item &cbm = *item::spawn( itype_power_storage_mkII ); cbm.faults.emplace( fault_bionic_nonsterile ); - body.components.push_back( cbm ); + body.components.push_back( &cbm ); } here.add_item_or_charges( pos(), body ); } @@ -10181,23 +10186,24 @@ void Character::place_corpse( const tripoint_abs_omt &om_target ) } std::vector tmp = inv_dump(); - item body = item::make_corpse( mtype_id::NULL_ID(), calendar::turn, name ); + item &body = item::make_corpse( mtype_id::NULL_ID(), calendar::turn, name ); for( auto itm : tmp ) { bay.add_item_or_charges( fin, *itm ); } for( const bionic &bio : *my_bionics ) { if( bio.info().itype().is_valid() ) { - body.put_in( item( bio.info().itype(), calendar::turn ) ); + //TODO!: check + body.put_in( *item::spawn( bio.info().itype(), calendar::turn ) ); } } // Restore amount of installed pseudo-modules of Power Storage Units std::pair storage_modules = amount_of_storage_bionics(); for( int i = 0; i < storage_modules.first; ++i ) { - body.put_in( item( "bio_power_storage" ) ); + body.put_in( *item::spawn( "bio_power_storage" ) ); } for( int i = 0; i < storage_modules.second; ++i ) { - body.put_in( item( "bio_power_storage_mkII" ) ); + body.put_in( *item::spawn( "bio_power_storage_mkII" ) ); } bay.add_item_or_charges( fin, body ); } @@ -10311,11 +10317,11 @@ std::vector Character::short_description_parts() const std::string gender = male ? _( "male" ) : _( "female" ); result.push_back( name + ", " + gender ); if( is_armed() ) { - result.push_back( _( "Wielding: " ) + weapon.tname() ); + result.push_back( _( "Wielding: " ) + weapon->tname() ); } const std::string worn_str = enumerate_as_string( worn.begin(), worn.end(), - []( const item & it ) { - return it.tname(); + []( const item * const & it ) { + return it->tname(); } ); if( !worn_str.empty() ) { result.push_back( _( "Wearing: " ) + worn_str ); diff --git a/src/character.h b/src/character.h index 862664e98ea2..de2350d45ae0 100644 --- a/src/character.h +++ b/src/character.h @@ -832,7 +832,7 @@ class Character : public Creature, public visitable /** Return the position in the worn list where new_item would be * put by default */ - std::list::iterator position_to_wear_new_item( const item &new_item ); + ItemList::iterator position_to_wear_new_item( const item &new_item ); /** Applies encumbrance from items only * If new_item is not null, then calculate under the asumption that it @@ -1081,7 +1081,7 @@ class Character : public Creature, public visitable // checks to see if an item is worn bool is_worn( const item &thing ) const { for( const auto &elem : worn ) { - if( &thing == &elem ) { + if( &thing == elem ) { return true; } } @@ -1106,7 +1106,7 @@ class Character : public Creature, public visitable * @param prompt optional message to display in any menu * @return whether the item was successfully disposed of */ - virtual bool dispose_item( item_location &&obj, const std::string &prompt = std::string() ); + virtual bool dispose_item( item &obj, const std::string &prompt = std::string() ); /** * Has the item enough charges to invoke its use function? @@ -1147,22 +1147,22 @@ class Character : public Creature, public visitable /** Wear item; returns nullopt on fail, or pointer to newly worn item on success. * If interactive is false, don't alert the player or drain moves on completion. */ - cata::optional::iterator> - wear_item( const item &to_wear, bool interactive = true ); + cata::optional + wear_item( item &to_wear, bool interactive = true ); /** Returns the amount of item `type' that is currently worn */ - int amount_worn( const itype_id &id ) const; + int amount_worn( const itype_id &id ) const; /** Returns nearby items which match the provided predicate */ - std::vector nearby( const std::function &func, - int radius = 1 ) const; + std::vector nearby( const std::function &func, + int radius = 1 ) const; /** * Similar to @ref remove_items_with, but considers only worn items and not their * content (@ref item::contents is not checked). * If the filter function returns true, the item is removed. */ - std::list remove_worn_items_with( std::function filter ); + ItemList remove_worn_items_with( std::function filter ); /** Return the item pointer of the item with given invlet, return nullptr if * the player does not have such an item with that invlet. Don't use this on npcs. @@ -1181,6 +1181,16 @@ class Character : public Creature, public visitable */ int get_item_position( const item *it ) const; + /** + * Returns the character's wielded item. + */ + item &get_weapon() const; + + /** + * Sets the character's weapon. + */ + void set_weapon( item &it ) const; + /** * Returns a reference to the item which will be used to make attacks. * At the moment it's always @ref weapon or a reference to a null item. @@ -1197,7 +1207,7 @@ class Character : public Creature, public visitable * @return Remaining charges which could not be stored in a container. */ int i_add_to_container( const item &it, bool unloading ); - item &i_add( item it, bool should_stack = true ); + item &i_add( item &it, bool should_stack = true ); /** * Try to pour the given liquid into the given container/vehicle. The transferred charges are @@ -1219,7 +1229,7 @@ class Character : public Creature, public visitable * exists, use @ref has_item to check this. * @return A copy of the removed item. */ - item i_rem( int pos ); + item &i_rem( int pos ); /** * Remove a specific item from player possession. The item is compared * by pointer. Contents of the item are removed as well. @@ -1227,7 +1237,7 @@ class Character : public Creature, public visitable * in the players possession (one can use @ref has_item to check for this). * @return A copy of the removed item. */ - item i_rem( const item *it ); + item &i_rem( const item *it ); void i_rem_keep_contents( int idx ); /** Sets invlet and adds to inventory if possible, drops otherwise, returns true if either succeeded. * An optional qty can be provided (and will perform better than separate calls). */ @@ -1241,13 +1251,13 @@ class Character : public Creature, public visitable * Whether the player carries an active item of the given item type. */ bool has_active_item( const itype_id &id ) const; - item remove_weapon(); + item &remove_weapon(); void remove_mission_items( int mission_id ); /** * Returns the items that are ammo and have the matching ammo type. */ - std::vector get_ammo( const ammotype &at ) const; + std::vector get_ammo( const ammotype &at ) const; /** * Searches for ammo or magazines that can be used to reload obj @@ -1255,12 +1265,12 @@ class Character : public Creature, public visitable * @param empty whether empty magazines should be considered as possible ammo * @param radius adjacent map/vehicle tiles to search. 0 for only player tile, -1 for only inventory */ - std::vector find_ammo( const item &obj, bool empty = true, int radius = 1 ) const; + std::vector find_ammo( const item &obj, bool empty = true, int radius = 1 ) const; /** * Searches for weapons and magazines that can be reloaded. */ - std::vector find_reloadables(); + std::vector find_reloadables(); /** * Counts ammo and UPS charges (lower of) for a given gun on the character. */ @@ -1303,7 +1313,7 @@ class Character : public Creature, public visitable * @param it Item we are checking * @param context optionally override effective item when checking contextual skills */ - bool can_use( const item &it, const item &context = item() ) const; + bool can_use( const item &it, const item *context = nullptr ) const; /** * Check character capable of wearing an item. * @param it Thing to be worn @@ -1338,7 +1348,7 @@ class Character : public Creature, public visitable /** Returns all items that must be taken off before taking off this item */ std::list get_dependent_worn_items( const item &it ) const; /** Drops an item to the specified location */ - void drop( item_location loc, const tripoint &where ); + void drop( item &loc, const tripoint &where ); virtual void drop( const drop_locations &what, const tripoint &target, bool stash = false ); virtual bool has_artifact_with( art_effect_passive effect ) const; @@ -1385,15 +1395,15 @@ class Character : public Creature, public visitable void mod_skill_level( const skill_id &ident, int delta ); /** Checks whether the character's skills meet the required */ bool meets_skill_requirements( const std::map &req, - const item &context = item() ) const; + const item *context = nullptr ) const; /** Checks whether the character's skills meet the required */ bool meets_skill_requirements( const construction &con ) const; /** Checks whether the character's stats meets the stats required by the item */ bool meets_stat_requirements( const item &it ) const; /** Checks whether the character meets overall requirements to be able to use the item */ - bool meets_requirements( const item &it, const item &context = item() ) const; + bool meets_requirements( const item &it, const item *context = nullptr ) const; /** Returns a string of missed requirements (both stats and skills) */ - std::string enumerate_unmet_requirements( const item &it, const item &context = item() ) const; + std::string enumerate_unmet_requirements( const item &it, const item *context = nullptr ) const; /** Returns the player's skill rust rate */ int rust_rate() const; @@ -1504,7 +1514,7 @@ class Character : public Creature, public visitable std::string name; bool male = true; - std::list worn; + ItemList worn; std::array damage_bandaged, damage_disinfected; bool nv_cached = false; // Means player sit inside vehicle on the tile he is now @@ -1518,7 +1528,6 @@ class Character : public Creature, public visitable cata::optional destination_point; inventory inv; itype_id last_item; - item weapon; int scent = 0; pimpl my_bionics; @@ -1538,7 +1547,8 @@ class Character : public Creature, public visitable weak_ptr_fast last_target; cata::optional last_target_pos; // Save favorite ammo location - item_location ammo_location; + //TODO!: ERRRM WUT? + item *ammo_location; std::set camps; /* crafting inventory cached time */ time_point cached_time; @@ -1569,21 +1579,21 @@ class Character : public Creature, public visitable /** * All items that have the given flag (@ref item::has_flag). */ - std::vector all_items_with_flag( const std::string &flag ) const; + std::vector all_items_with_flag( const std::string &flag ) const; bool has_charges( const itype_id &it, int quantity, const std::function &filter = return_true ) const; // has_amount works ONLY for quantity. // has_charges works ONLY for charges. - std::list use_amount( itype_id it, int quantity, - const std::function &filter = return_true ); + ItemList use_amount( itype_id it, int quantity, + const std::function &filter = return_true ); // Uses up charges bool use_charges_if_avail( const itype_id &it, int quantity ); // Uses up charges - std::list use_charges( const itype_id &what, int qty, - const std::function &filter = return_true ); + ItemList use_charges( const itype_id &what, int qty, + const std::function &filter = return_true ); bool has_fire( int quantity ) const; void use_fire( int quantity ); @@ -1925,7 +1935,7 @@ class Character : public Creature, public visitable /** Swap side on which item is worn; returns false on fail. If interactive is false, don't alert player or drain moves */ bool change_side( item &it, bool interactive = true ); - bool change_side( item_location &loc, bool interactive = true ); + bool change_side( item *it, bool interactive = true ); bool get_check_encumbrance() { return check_encumbrance; @@ -2168,6 +2178,8 @@ class Character : public Creature, public visitable // A unique ID number, assigned by the game class. Values should never be reused. character_id id; + item *weapon; + units::energy power_level; units::energy max_power_level; diff --git a/src/character_functions.cpp b/src/character_functions.cpp index b2f98a16f13b..db376274f24d 100644 --- a/src/character_functions.cpp +++ b/src/character_functions.cpp @@ -51,7 +51,7 @@ void siphon( Character &ch, vehicle &veh, const itype_id &desired_liquid ) return; } - item liquid( desired_liquid, calendar::turn, qty ); + item &liquid = *item::spawn( desired_liquid, calendar::turn, qty ); if( liquid_handler::handle_liquid( liquid, nullptr, 1, nullptr, &veh ) ) { veh.drain( desired_liquid, qty - liquid.charges ); } diff --git a/src/character_martial_arts.cpp b/src/character_martial_arts.cpp index 4cc166fbb593..56d934d66dfb 100644 --- a/src/character_martial_arts.cpp +++ b/src/character_martial_arts.cpp @@ -118,7 +118,7 @@ std::string character_martial_arts::enumerate_known_styles( const itype_id &weap std::string character_martial_arts::selected_style_name( const Character &owner ) const { - if( style_selected->force_unarmed || style_selected->weapon_valid( owner.weapon ) ) { + if( style_selected->force_unarmed || style_selected->weapon_valid( owner.get_weapon() ) ) { return style_selected->name.translated(); } else if( owner.is_armed() ) { return _( "Normal" ); diff --git a/src/character_oracle.cpp b/src/character_oracle.cpp index 405e0ec78c1c..c0b9f969817b 100644 --- a/src/character_oracle.cpp +++ b/src/character_oracle.cpp @@ -59,8 +59,8 @@ status_t character_oracle_t::can_wear_warmer_clothes() const // Check inventory for wearable warmer clothes, greedily. // Don't consider swapping clothes yet, just evaluate adding clothes. for( const auto &i : subject->inv.const_slice() ) { - const item &candidate = i->front(); - if( candidate.get_warmth() > 0 || p->can_wear( candidate ).success() ) { + const item *const &candidate = i->front(); + if( candidate->get_warmth() > 0 || p->can_wear( *candidate ).success() ) { return running; } } @@ -73,13 +73,13 @@ status_t character_oracle_t::can_make_fire() const bool tool = false; bool fuel = false; for( const auto &i : subject->inv.const_slice() ) { - const item &candidate = i->front(); - if( candidate.has_flag( flag_FIRESTARTER ) ) { + const item *const &candidate = i->front(); + if( candidate->has_flag( flag_FIRESTARTER ) ) { tool = true; if( fuel ) { return running; } - } else if( candidate.flammable() ) { + } else if( candidate->flammable() ) { fuel = true; if( tool ) { return running; diff --git a/src/clzones.cpp b/src/clzones.cpp index 5568da4b470b..d1456f81c2c1 100644 --- a/src/clzones.cpp +++ b/src/clzones.cpp @@ -293,9 +293,9 @@ plot_options::query_seed_result plot_options::query_seed() here.getabs( p.pos() ), 60 ); for( const tripoint &elem : zone_src_set ) { tripoint elem_loc = here.getlocal( elem ); - for( item &it : here.i_at( elem_loc ) ) { - if( it.is_seed() ) { - seed_inv.push_back( &it ); + for( item * const &it : here.i_at( elem_loc ) ) { + if( it->is_seed() ) { + seed_inv.push_back( it ); } } } @@ -309,9 +309,8 @@ plot_options::query_seed_result plot_options::query_seed() const itype_id &new_seed = std::get<0>( seed_entry ); itype_id new_mark; - item it = item( new_seed ); - if( it.is_seed() ) { - new_mark = it.type->seed->fruit_id; + if( new_seed->is_seed() ) { + new_mark = new_seed->seed->fruit_id; } else { new_mark = seed; } @@ -407,9 +406,8 @@ std::string plot_options::get_zone_name_suggestion() const { if( !seed.is_empty() ) { auto type = itype_id( seed ); - item it = item( type ); - if( it.is_seed() ) { - return it.type->seed->plant_name.translated(); + if( seed->is_seed() ) { + return seed->seed->plant_name.translated(); } else { return item::nname( type ); } diff --git a/src/colony.h b/src/colony.h index f335258958c0..15d9e523d7cd 100644 --- a/src/colony.h +++ b/src/colony.h @@ -1,3517 +1,19 @@ -// This software is a modified version of the original. -// The original license is as follows: - -// Copyright (c) 2019, Matthew Bentley (mattreecebentley@gmail.com) www.plflib.org - -// zLib license (https://www.zlib.net/zlib_license.html): -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgement in the product documentation would be -// appreciated but is not required. -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// 3. This notice may not be removed or altered from any source distribution. - #pragma once #ifndef CATA_SRC_COLONY_H #define CATA_SRC_COLONY_H -// Compiler-specific defines used by colony: -#define COLONY_CONSTEXPR -#define COLONY_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept -#define COLONY_NOEXCEPT_SWAP(the_allocator) noexcept - -// TODO: Switch to these when we move to C++17 -// #define COLONY_CONSTEXPR constexpr -// #define COLONY_NOEXCEPT_MOVE_ASSIGNMENT(the_allocator) noexcept(std::allocator_traits::is_always_equal::value) -// #define COLONY_NOEXCEPT_SWAP(the_allocator) noexcept(std::allocator_traits::propagate_on_container_swap::value) - -// Note: GCC creates faster code without forcing inline -#if defined(_MSC_VER) -#define COLONY_FORCE_INLINE __forceinline -#else -#define COLONY_FORCE_INLINE -#endif - -/* whole GCC 6 family */ -#if __GNUC__ == 6 -/* GCC 6.5 at least complains about type punning, but nothing else does. */ -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#endif - -// TODO: get rid of these defines -#define COLONY_CONSTRUCT(the_allocator, allocator_instance, location, ...) std::allocator_traits::construct(allocator_instance, location, __VA_ARGS__) -#define COLONY_DESTROY(the_allocator, allocator_instance, location) std::allocator_traits::destroy(allocator_instance, location) -#define COLONY_ALLOCATE(the_allocator, allocator_instance, size, hint) std::allocator_traits::allocate(allocator_instance, size, hint) -#define COLONY_ALLOCATE_INITIALIZATION(the_allocator, size, hint) std::allocator_traits::allocate(*this, size, hint) -#define COLONY_DEALLOCATE(the_allocator, allocator_instance, location, size) std::allocator_traits::deallocate(allocator_instance, location, size) - -#include // std::sort and std::fill_n -#include // assert -#include // offsetof, used in blank() -#include // memset, memcpy -#include -#include // std::bidirectional_iterator_tag -#include // std::numeric_limits -#include // std::allocator -#include // std::is_trivially_destructible, etc -#include // std::move +#include +#include +class item; namespace cata { +//TODO!: Lmao +template +using colony = std::vector; -template , typename element_skipfield_type = unsigned short > -// Empty base class optimization - inheriting allocator functions -class colony : private element_allocator_type -// Note: unsigned short is equivalent to uint_least16_t i.e. Using 16-bit unsigned integer in best-case scenario, greater-than-16-bit unsigned integer where platform doesn't support 16-bit types -{ - public: - // Standard container typedefs: - using value_type = element_type; - using allocator_type = element_allocator_type; - using skipfield_type = element_skipfield_type; - - using aligned_element_type = typename std::aligned_storage < sizeof( element_type ), - ( alignof( element_type ) > ( sizeof( element_skipfield_type ) * 2 ) ) ? alignof( element_type ) : - ( sizeof( element_skipfield_type ) * 2 ) >::type; - - using size_type = typename std::allocator_traits::size_type; - using difference_type = typename std::allocator_traits::difference_type; - using reference = element_type&; - using const_reference = const element_type&; - using pointer = typename std::allocator_traits::pointer; - using const_pointer = typename std::allocator_traits::const_pointer; - - // Iterator declarations: - template class colony_iterator; - using iterator = colony_iterator; - using const_iterator = colony_iterator; - friend class colony_iterator; // Using above typedef name here is illegal under C++03 - friend class colony_iterator; - - template class colony_reverse_iterator; - using reverse_iterator = colony_reverse_iterator; - using const_reverse_iterator = colony_reverse_iterator; - friend class colony_reverse_iterator; - friend class colony_reverse_iterator; - - private: - - struct group; // forward declaration for typedefs below - - using aligned_element_allocator_type = typename - std::allocator_traits::template rebind_alloc; - using group_allocator_type = typename std::allocator_traits::template - rebind_alloc; - using skipfield_allocator_type = typename std::allocator_traits::template - rebind_alloc; - - // Using uchar as the generic allocator type, as sizeof is always guaranteed to be 1 byte regardless of the number of bits in a byte on given computer, whereas for example, uint8_t would fail on machines where there are more than 8 bits in a byte eg. Texas Instruments C54x DSPs. - using uchar_allocator_type = typename std::allocator_traits::template - rebind_alloc; - // Different typedef to 'pointer' - this is a pointer to the over aligned element type, not the original element type - using aligned_pointer_type = typename - std::allocator_traits::pointer; - using group_pointer_type = typename std::allocator_traits::pointer; - using skipfield_pointer_type = typename std::allocator_traits::pointer; - using uchar_pointer_type = typename std::allocator_traits::pointer; - - using pointer_allocator_type = typename std::allocator_traits::template - rebind_alloc; - - // Colony groups: - - // Empty base class optimization (EBCO) - inheriting allocator functions - struct group : private uchar_allocator_type { - aligned_pointer_type - last_endpoint; // The address that is one past the highest cell number that's been used so far in this group - does not change with erase command but may change with insert (if no previously-erased locations are available) - is necessary because an iterator cannot access the colony's end_iterator. Most-used variable in colony use (operator ++, --) so first in struct - group_pointer_type - next_group; // Next group in the intrusive list of all groups. NULL if no next group - const aligned_pointer_type elements; // Element storage - const skipfield_pointer_type - skipfield; // Skipfield storage. The element and skipfield arrays are allocated contiguously, hence the skipfield pointer also functions as a 'one-past-end' pointer for the elements array. There will always be one additional skipfield node allocated compared to the number of elements. This is to ensure a faster ++ iterator operation (fewer checks are required when this is present). The extra node is unused and always zero, but checked, and not having it will result in out-of-bounds memory errors. - group_pointer_type - previous_group; // previous group in the intrusive list of all groups. NULL if no preceding group - skipfield_type - free_list_head; // The index of the last erased element in the group. The last erased element will, in turn, contain the number of the index of the next erased element, and so on. If this is == maximum skipfield_type value then free_list is empty i.e. no erasures have occurred in the group (or if they have, the erased locations have then been reused via insert()). - const skipfield_type - capacity; // The element capacity of this particular group - skipfield_type - number_of_elements; // indicates total number of active elements in group - changes with insert and erase commands - used to check for empty group in erase function, as an indication to remove the group - group_pointer_type - erasures_list_next_group; // The next group in the intrusive singly-linked list of groups with erasures i.e. with active erased-element free lists - size_type - group_number; // Used for comparison (> < >= <=) iterator operators (used by distance function and user) - - group( const skipfield_type elements_per_group, group_pointer_type const previous = nullptr ): - last_endpoint( reinterpret_cast( COLONY_ALLOCATE_INITIALIZATION( - uchar_allocator_type, ( ( elements_per_group * ( sizeof( aligned_element_type ) ) ) + ( ( - elements_per_group + 1u ) * sizeof( skipfield_type ) ) ), - ( previous == nullptr ) ? nullptr : - previous->elements ) ) ), /* allocating to here purely because it is first in the struct sequence - actual pointer is elements, last_endpoint is only initialized to element's base value initially, then incremented by one below */ - next_group( nullptr ), - elements( last_endpoint++ ), - skipfield( reinterpret_cast( elements + elements_per_group ) ), - previous_group( previous ), - free_list_head( std::numeric_limits::max() ), - capacity( elements_per_group ), - number_of_elements( 1 ), - erasures_list_next_group( nullptr ), - group_number( ( previous == nullptr ) ? 0 : previous->group_number + 1u ) { - // Static casts to unsigned int from short not necessary as C++ automatically promotes lesser types for arithmetic purposes. - std::memset( &*skipfield, 0, sizeof( skipfield_type ) * ( elements_per_group + - 1u ) ); // &* to avoid problems with non-trivial pointers - } - - ~group() noexcept { - // Null check not necessary (for copied group as above) as delete will also perform a null check. - COLONY_DEALLOCATE( uchar_allocator_type, ( *this ), - reinterpret_cast( elements ), - ( capacity * sizeof( aligned_element_type ) ) + ( ( capacity + 1u ) * sizeof( skipfield_type ) ) ); - } - }; - - // Implement const/non-const iterator switching pattern: - template struct choose; - - template struct choose { - using type = is_true; - }; - - template struct choose { - using type = is_false; - }; - - public: - - // Iterators: - template class colony_iterator - { - private: - group_pointer_type group_pointer; - aligned_pointer_type element_pointer; - skipfield_pointer_type skipfield_pointer; - - public: - using iterator_category = std::bidirectional_iterator_tag; - using value_type = typename colony::value_type; - using difference_type = typename colony::difference_type; - using pointer = typename - choose::type; - using reference = typename - choose::type; - - friend class colony; - friend class colony_reverse_iterator; - friend class colony_reverse_iterator; - - inline colony_iterator &operator=( const colony_iterator &source ) noexcept { - group_pointer = source.group_pointer; - element_pointer = source.element_pointer; - skipfield_pointer = source.skipfield_pointer; - return *this; - } - - inline colony_iterator &operator=( const colony_iterator < !is_const > &source ) noexcept { - group_pointer = source.group_pointer; - element_pointer = source.element_pointer; - skipfield_pointer = source.skipfield_pointer; - return *this; - } - - // Move assignment - only really necessary if the allocator uses non-standard i.e. smart pointers - inline colony_iterator &operator=( colony_iterator &&source ) - noexcept { // Move is a copy in this scenario - assert( &source != this ); - group_pointer = std::move( source.group_pointer ); - element_pointer = std::move( source.element_pointer ); - skipfield_pointer = std::move( source.skipfield_pointer ); - return *this; - } - - inline colony_iterator &operator=( colony_iterator < !is_const > &&source ) noexcept { - assert( &source != this ); - group_pointer = std::move( source.group_pointer ); - element_pointer = std::move( source.element_pointer ); - skipfield_pointer = std::move( source.skipfield_pointer ); - return *this; - } - - inline COLONY_FORCE_INLINE bool operator==( const colony_iterator &rh ) const noexcept { - return ( element_pointer == rh.element_pointer ); - } - - inline COLONY_FORCE_INLINE bool operator==( const colony_iterator < !is_const > &rh ) const - noexcept { - return ( element_pointer == rh.element_pointer ); - } - - inline COLONY_FORCE_INLINE bool operator!=( const colony_iterator &rh ) const noexcept { - return ( element_pointer != rh.element_pointer ); - } - - inline COLONY_FORCE_INLINE bool operator!=( const colony_iterator < !is_const > &rh ) const - noexcept { - return ( element_pointer != rh.element_pointer ); - } - - // may cause exception with uninitialized iterator - inline COLONY_FORCE_INLINE reference operator*() const { - return *( reinterpret_cast( element_pointer ) ); - } - - inline COLONY_FORCE_INLINE pointer operator->() const noexcept { - return reinterpret_cast( element_pointer ); - } - - colony_iterator &operator++() { - // covers uninitialized colony_iterator - assert( group_pointer != nullptr ); - // Assert that iterator is not already at end() - assert( !( element_pointer == group_pointer->last_endpoint && - group_pointer->next_group != nullptr ) ); - - skipfield_type skip = *( ++skipfield_pointer ); - - // ie. beyond end of available data - if( ( element_pointer += skip + 1 ) == group_pointer->last_endpoint && - group_pointer->next_group != nullptr ) { - group_pointer = group_pointer->next_group; - skip = *( group_pointer->skipfield ); - element_pointer = group_pointer->elements + skip; - skipfield_pointer = group_pointer->skipfield; - } - - skipfield_pointer += skip; - return *this; - } - - inline colony_iterator operator++( int ) { - const colony_iterator copy( *this ); - ++*this; - return copy; - } - - private: - inline COLONY_FORCE_INLINE void check_for_end_of_group_and_progress() { // used by erase - if( element_pointer == group_pointer->last_endpoint && group_pointer->next_group != nullptr ) { - group_pointer = group_pointer->next_group; - skipfield_pointer = group_pointer->skipfield; - element_pointer = group_pointer->elements + *skipfield_pointer; - skipfield_pointer += *skipfield_pointer; - } - } - - public: - - colony_iterator &operator--() { - assert( group_pointer != nullptr ); - assert( !( element_pointer == group_pointer->elements && - group_pointer->previous_group == - nullptr ) ); // Assert that we are not already at begin() - this is not required to be tested in the code below as we don't need a special condition to progress to begin(), like we do with end() in operator ++ - - if( element_pointer != group_pointer->elements ) { // i.e. not already at beginning of group - const skipfield_type skip = *( --skipfield_pointer ); - skipfield_pointer -= skip; - - if( ( element_pointer -= skip + 1 ) != group_pointer->elements - - 1 ) { // i.e. iterator was not already at beginning of colony (with some previous consecutive deleted elements), and skipfield does not takes us into the previous group) - return *this; - } - } - - group_pointer = group_pointer->previous_group; - skipfield_pointer = group_pointer->skipfield + group_pointer->capacity - 1; - element_pointer = ( reinterpret_cast( group_pointer->skipfield ) - 1 ) - - *skipfield_pointer; - skipfield_pointer -= *skipfield_pointer; - - return *this; - } - - inline colony_iterator operator--( int ) { - const colony_iterator copy( *this ); - --*this; - return copy; - } - - inline bool operator>( const colony_iterator &rh ) const noexcept { - return ( ( group_pointer == rh.group_pointer ) & ( element_pointer > rh.element_pointer ) ) || - ( group_pointer != rh.group_pointer && - group_pointer->group_number > rh.group_pointer->group_number ); - } - - inline bool operator<( const colony_iterator &rh ) const noexcept { - return rh > *this; - } - - inline bool operator>=( const colony_iterator &rh ) const noexcept { - return !( rh > *this ); - } - - inline bool operator<=( const colony_iterator &rh ) const noexcept { - return !( *this > rh ); - } - - inline bool operator>( const colony_iterator < !is_const > &rh ) const noexcept { - return ( ( group_pointer == rh.group_pointer ) & ( element_pointer > rh.element_pointer ) ) || - ( group_pointer != rh.group_pointer && - group_pointer->group_number > rh.group_pointer->group_number ); - } - - inline bool operator<( const colony_iterator < !is_const > &rh ) const noexcept { - return rh > *this; - } - - inline bool operator>=( const colony_iterator < !is_const > &rh ) const noexcept { - return !( rh > *this ); - } - - inline bool operator<=( const colony_iterator < !is_const > &rh ) const noexcept { - return !( *this > rh ); - } - - colony_iterator() noexcept: group_pointer( nullptr ), element_pointer( nullptr ), - skipfield_pointer( nullptr ) {} - - private: - // Used by cend(), erase() etc: - colony_iterator( const group_pointer_type group_p, const aligned_pointer_type element_p, - const skipfield_pointer_type skipfield_p ) noexcept: group_pointer( group_p ), - element_pointer( element_p ), skipfield_pointer( skipfield_p ) {} - - public: - - inline colony_iterator( const colony_iterator &source ) noexcept: - group_pointer( source.group_pointer ), - element_pointer( source.element_pointer ), - skipfield_pointer( source.skipfield_pointer ) {} - - inline colony_iterator( const colony_iterator < !is_const > &source ) noexcept: - group_pointer( source.group_pointer ), - element_pointer( source.element_pointer ), - skipfield_pointer( source.skipfield_pointer ) {} - - // move constructor - inline colony_iterator( colony_iterator &&source ) noexcept: - group_pointer( std::move( source.group_pointer ) ), - element_pointer( std::move( source.element_pointer ) ), - skipfield_pointer( std::move( source.skipfield_pointer ) ) {} - - inline colony_iterator( colony_iterator < !is_const > &&source ) noexcept: - group_pointer( std::move( source.group_pointer ) ), - element_pointer( std::move( source.element_pointer ) ), - skipfield_pointer( std::move( source.skipfield_pointer ) ) {} - }; // colony_iterator - - // Reverse iterators: - template class colony_reverse_iterator - { - private: - iterator it; - - public: - using iterator_category = std::bidirectional_iterator_tag; - using value_type = typename colony::value_type; - using difference_type = typename colony::difference_type; - using pointer = typename - choose::type; - using reference = typename - choose::type; - - friend class colony; - - inline colony_reverse_iterator &operator=( const colony_reverse_iterator &source ) noexcept { - it = source.it; - return *this; - } - - // move assignment - inline colony_reverse_iterator &operator=( colony_reverse_iterator &&source ) noexcept { - it = std::move( source.it ); - return *this; - } - - inline COLONY_FORCE_INLINE bool operator==( const colony_reverse_iterator &rh ) const noexcept { - return ( it == rh.it ); - } - - inline COLONY_FORCE_INLINE bool operator!=( const colony_reverse_iterator &rh ) const noexcept { - return ( it != rh.it ); - } - - inline COLONY_FORCE_INLINE reference operator*() const noexcept { - return *( reinterpret_cast( it.element_pointer ) ); - } - - inline COLONY_FORCE_INLINE pointer *operator->() const noexcept { - return reinterpret_cast( it.element_pointer ); - } - - // In this case we have to redefine the algorithm, rather than using the internal iterator's -- operator, in order for the reverse_iterator to be allowed to reach rend() ie. begin_iterator - 1 - colony_reverse_iterator &operator++() { - colony::group_pointer_type &group_pointer = it.group_pointer; - colony::aligned_pointer_type &element_pointer = it.element_pointer; - colony::skipfield_pointer_type &skipfield_pointer = it.skipfield_pointer; - - assert( group_pointer != nullptr ); - assert( !( element_pointer == group_pointer->elements - 1 && - group_pointer->previous_group == nullptr ) ); // Assert that we are not already at rend() - - if( element_pointer != group_pointer->elements ) { // ie. not already at beginning of group - element_pointer -= *( --skipfield_pointer ) + 1; - skipfield_pointer -= *skipfield_pointer; - - if( !( element_pointer == group_pointer->elements - 1 && - group_pointer->previous_group == nullptr ) ) { // i.e. iterator is not == rend() - return *this; - } - } - - if( group_pointer->previous_group != nullptr ) { // i.e. not first group in colony - group_pointer = group_pointer->previous_group; - skipfield_pointer = group_pointer->skipfield + group_pointer->capacity - 1; - element_pointer = ( reinterpret_cast( group_pointer->skipfield ) - 1 ) - - *skipfield_pointer; - skipfield_pointer -= *skipfield_pointer; - } else { // necessary so that reverse_iterator can end up == rend(), if we were already at first element in colony - --element_pointer; - --skipfield_pointer; - } - - return *this; - } - - inline colony_reverse_iterator operator++( int ) { - const colony_reverse_iterator copy( *this ); - ++*this; - return copy; - } - - inline COLONY_FORCE_INLINE colony_reverse_iterator &operator--() { - // i.e. Check that we are not already at rbegin() - assert( !( it.element_pointer == it.group_pointer->last_endpoint - 1 && - it.group_pointer->next_group == nullptr ) ); - ++it; - return *this; - } - - inline colony_reverse_iterator operator--( int ) { - const colony_reverse_iterator copy( *this ); - --*this; - return copy; - } - - inline typename colony::iterator base() const { - return ++( typename colony::iterator( it ) ); - } - - inline bool operator>( const colony_reverse_iterator &rh ) const noexcept { - return ( rh.it > it ); - } - - inline bool operator<( const colony_reverse_iterator &rh ) const noexcept { - return ( it > rh.it ); - } - - inline bool operator>=( const colony_reverse_iterator &rh ) const noexcept { - return !( it > rh.it ); - } - - inline bool operator<=( const colony_reverse_iterator &rh ) const noexcept { - return !( rh.it > it ); - } - - inline COLONY_FORCE_INLINE bool operator==( const colony_reverse_iterator < !r_is_const > &rh ) - const noexcept { - return ( it == rh.it ); - } - - inline COLONY_FORCE_INLINE bool operator!=( const colony_reverse_iterator < !r_is_const > &rh ) - const noexcept { - return ( it != rh.it ); - } - - inline bool operator>( const colony_reverse_iterator < !r_is_const > &rh ) const noexcept { - return ( rh.it > it ); - } - - inline bool operator<( const colony_reverse_iterator < !r_is_const > &rh ) const noexcept { - return ( it > rh.it ); - } - - inline bool operator>=( const colony_reverse_iterator < !r_is_const > &rh ) const noexcept { - return !( it > rh.it ); - } - - inline bool operator<=( const colony_reverse_iterator < !r_is_const > &rh ) const noexcept { - return !( rh.it > it ); - } - - colony_reverse_iterator() noexcept = default; - - colony_reverse_iterator( const colony_reverse_iterator &source ) noexcept: - it( source.it ) {} - - colony_reverse_iterator( const typename colony::iterator &source ) noexcept: - it( source ) {} - - private: - // Used by rend(), etc: - colony_reverse_iterator( const group_pointer_type group_p, const aligned_pointer_type element_p, - const skipfield_pointer_type skipfield_p ) noexcept: - it( group_p, element_p, skipfield_p ) {} - - public: - - // move constructors - colony_reverse_iterator( colony_reverse_iterator &&source ) noexcept: - it( std::move( source.it ) ) {} - - colony_reverse_iterator( typename colony::iterator &&source ) noexcept: - it( std::move( source ) ) {} - - }; // colony_reverse_iterator - - private: - - // Used to prevent fill-insert/constructor calls being mistakenly resolved to range-insert/constructor calls - template - struct enable_if_c { - using type = T; - }; - - template - struct enable_if_c { - }; - - iterator end_iterator, begin_iterator; - - // Head of a singly-linked intrusive list of groups which have erased-element memory locations available for reuse - group_pointer_type groups_with_erasures_list_head; - size_type total_number_of_elements, total_capacity; - - // Packaging the element pointer allocator with a lesser-used member variable, for empty-base-class optimization - struct ebco_pair2 : pointer_allocator_type { - skipfield_type min_elements_per_group; - explicit ebco_pair2( const skipfield_type min_elements ) noexcept: - min_elements_per_group( min_elements ) {} - } pointer_allocator_pair; - - struct ebco_pair : group_allocator_type { - skipfield_type max_elements_per_group; - explicit ebco_pair( const skipfield_type max_elements ) noexcept: - max_elements_per_group( max_elements ) {} - } group_allocator_pair; - - public: - - /** - * Default constructor: - * default minimum group size is 8, default maximum group size is - * std::numeric_limits::max() (typically 65535). You cannot set the group - * sizes from the constructor in this scenario, but you can call the change_group_sizes() - * member function after construction has occurred. - */ - colony() noexcept: - element_allocator_type( element_allocator_type() ), - groups_with_erasures_list_head( nullptr ), - total_number_of_elements( 0 ), - total_capacity( 0 ), - pointer_allocator_pair( ( sizeof( aligned_element_type ) * 8 > ( sizeof( *this ) + sizeof( - group ) ) * 2 ) ? 8 : ( ( ( sizeof( *this ) + sizeof( group ) ) * 2 ) / sizeof( - aligned_element_type ) ) ), - group_allocator_pair( std::numeric_limits::max() ) { - // skipfield type must be of unsigned integer type (uchar, ushort, uint etc) - assert( std::numeric_limits::is_integer & - !std::numeric_limits::is_signed ); - } - - /** - * Default constructor, but using a custom memory allocator eg. something other than - * std::allocator. - */ - explicit colony( const element_allocator_type &alloc ): - element_allocator_type( alloc ), - groups_with_erasures_list_head( NULL ), - total_number_of_elements( 0 ), - total_capacity( 0 ), - pointer_allocator_pair( ( sizeof( aligned_element_type ) * 8 > ( sizeof( *this ) + sizeof( - group ) ) * 2 ) ? 8 : ( ( ( sizeof( *this ) + sizeof( group ) ) * 2 ) / sizeof( - aligned_element_type ) ) ), - group_allocator_pair( std::numeric_limits::max() ) { - assert( std::numeric_limits::is_integer & - !std::numeric_limits::is_signed ); - } - - /** - * Copy constructor: - * Copy all contents from source colony, removes any empty (erased) element locations in the - * process. Size of groups created is either the total size of the source colony, or the - * maximum group size of the source colony, whichever is the smaller. - */ - colony( const colony &source ): - element_allocator_type( source ), - groups_with_erasures_list_head( nullptr ), - total_number_of_elements( 0 ), - total_capacity( 0 ), - // Make the first colony group capacity the greater of min_elements_per_group or total_number_of_elements, so long as total_number_of_elements isn't larger than max_elements_per_group - pointer_allocator_pair( static_cast( ( - source.pointer_allocator_pair.min_elements_per_group > source.total_number_of_elements ) ? - source.pointer_allocator_pair.min_elements_per_group : ( ( source.total_number_of_elements > - source.group_allocator_pair.max_elements_per_group ) ? - source.group_allocator_pair.max_elements_per_group : - source.total_number_of_elements ) ) ), - group_allocator_pair( source.group_allocator_pair.max_elements_per_group ) { - insert( source.begin_iterator, source.end_iterator ); - // reset to correct value for future clear() or erasures - pointer_allocator_pair.min_elements_per_group = - source.pointer_allocator_pair.min_elements_per_group; - } - - // Copy constructor (allocator-extended): - colony( const colony &source, const allocator_type &alloc ): - element_allocator_type( alloc ), - groups_with_erasures_list_head( nullptr ), - total_number_of_elements( 0 ), - total_capacity( 0 ), - pointer_allocator_pair( static_cast( ( - source.pointer_allocator_pair.min_elements_per_group > source.total_number_of_elements ) ? - source.pointer_allocator_pair.min_elements_per_group : ( ( source.total_number_of_elements > - source.group_allocator_pair.max_elements_per_group ) ? - source.group_allocator_pair.max_elements_per_group : source.total_number_of_elements ) ) ), - group_allocator_pair( source.group_allocator_pair.max_elements_per_group ) { - insert( source.begin_iterator, source.end_iterator ); - pointer_allocator_pair.min_elements_per_group = - source.pointer_allocator_pair.min_elements_per_group; - } - - private: - - inline void blank() noexcept { - // if all pointer types are trivial, we can just nuke it from orbit with memset (NULL is always 0 in C++): - if COLONY_CONSTEXPR( std::is_trivial::value && - std::is_trivial::value && - std::is_trivial::value ) { - std::memset( static_cast( this ), 0, offsetof( colony, pointer_allocator_pair ) ); - } else { - end_iterator.group_pointer = nullptr; - end_iterator.element_pointer = nullptr; - end_iterator.skipfield_pointer = nullptr; - begin_iterator.group_pointer = nullptr; - begin_iterator.element_pointer = nullptr; - begin_iterator.skipfield_pointer = nullptr; - groups_with_erasures_list_head = nullptr; - total_number_of_elements = 0; - total_capacity = 0; - } - } - - public: - - /** - * Move constructor: - * Move all contents from source colony, does not remove any erased element locations or - * alter any of the source group sizes. Source colony is now empty and can be safely - * destructed or otherwise used. - */ - colony( colony &&source ) noexcept: - element_allocator_type( source ), - end_iterator( std::move( source.end_iterator ) ), - begin_iterator( std::move( source.begin_iterator ) ), - groups_with_erasures_list_head( std::move( source.groups_with_erasures_list_head ) ), - total_number_of_elements( source.total_number_of_elements ), - total_capacity( source.total_capacity ), - pointer_allocator_pair( source.pointer_allocator_pair.min_elements_per_group ), - group_allocator_pair( source.group_allocator_pair.max_elements_per_group ) { - source.blank(); - } - - // Move constructor (allocator-extended): - colony( colony &&source, const allocator_type &alloc ): - element_allocator_type( alloc ), - end_iterator( std::move( source.end_iterator ) ), - begin_iterator( std::move( source.begin_iterator ) ), - groups_with_erasures_list_head( std::move( source.groups_with_erasures_list_head ) ), - total_number_of_elements( source.total_number_of_elements ), - total_capacity( source.total_capacity ), - pointer_allocator_pair( source.pointer_allocator_pair.min_elements_per_group ), - group_allocator_pair( source.group_allocator_pair.max_elements_per_group ) { - source.blank(); - } - - /** - * Fill constructor with value_type unspecified, so the value_type's default constructor is - * used. n specifies the number of elements to create upon construction. If n is larger than - * min_group_size, the size of the groups created will either be n and max_group_size, - * depending on which is smaller. min_group_size (i.e. the smallest possible number of - * elements which can be stored in a colony group) can be defined, as can the max_group_size. - * Setting the group sizes can be a performance advantage if you know in advance roughly how - * many objects are likely to be stored in your colony long-term - or at least the rough - * scale of storage. If that case, using this can stop many small initial groups being - * allocated (reserve() will achieve a similar result, but structurally at the moment is - * limited to allocating one group). - */ - colony( const size_type fill_number, const element_type &element, - const skipfield_type min_allocation_amount = 0, - const skipfield_type max_allocation_amount = std::numeric_limits::max(), - const element_allocator_type &alloc = element_allocator_type() ): - element_allocator_type( alloc ), - groups_with_erasures_list_head( nullptr ), - total_number_of_elements( 0 ), - total_capacity( 0 ), - pointer_allocator_pair( ( min_allocation_amount != 0 ) ? min_allocation_amount : - ( fill_number > max_allocation_amount ) ? max_allocation_amount : - ( fill_number > 8 ) ? static_cast( fill_number ) : 8 ), - group_allocator_pair( max_allocation_amount ) { - assert( std::numeric_limits::is_integer & - !std::numeric_limits::is_signed ); - assert( ( pointer_allocator_pair.min_elements_per_group > 2 ) & - ( pointer_allocator_pair.min_elements_per_group <= group_allocator_pair.max_elements_per_group ) ); - - insert( fill_number, element ); - } - - // Range constructor: - template - colony( const typename enable_if_c < !std::numeric_limits::is_integer, - iterator_type >::type &first, const iterator_type &last, - const skipfield_type min_allocation_amount = 8, - const skipfield_type max_allocation_amount = std::numeric_limits::max(), - const element_allocator_type &alloc = element_allocator_type() ): - element_allocator_type( alloc ), - groups_with_erasures_list_head( nullptr ), - total_number_of_elements( 0 ), - total_capacity( 0 ), - pointer_allocator_pair( min_allocation_amount ), - group_allocator_pair( max_allocation_amount ) { - assert( std::numeric_limits::is_integer & - !std::numeric_limits::is_signed ); - assert( ( pointer_allocator_pair.min_elements_per_group > 2 ) & - ( pointer_allocator_pair.min_elements_per_group <= group_allocator_pair.max_elements_per_group ) ); - - insert( first, last ); - } - - // Initializer-list constructor: - colony( const std::initializer_list &element_list, - const skipfield_type min_allocation_amount = 0, - const skipfield_type max_allocation_amount = std::numeric_limits::max(), - const element_allocator_type &alloc = element_allocator_type() ): - element_allocator_type( alloc ), - groups_with_erasures_list_head( nullptr ), - total_number_of_elements( 0 ), - total_capacity( 0 ), - pointer_allocator_pair( ( min_allocation_amount != 0 ) ? min_allocation_amount : - ( element_list.size() > max_allocation_amount ) ? max_allocation_amount : - ( element_list.size() > 8 ) ? static_cast( element_list.size() ) : 8 ), - group_allocator_pair( max_allocation_amount ) { - assert( std::numeric_limits::is_integer & - !std::numeric_limits::is_signed ); - assert( ( pointer_allocator_pair.min_elements_per_group > 2 ) & - ( pointer_allocator_pair.min_elements_per_group <= group_allocator_pair.max_elements_per_group ) ); - - insert( element_list ); - } - - inline COLONY_FORCE_INLINE iterator begin() noexcept { - return begin_iterator; - } - - inline COLONY_FORCE_INLINE const iterator &begin() const - noexcept { // To allow for functions which only take const colony & as a source eg. copy constructor - return begin_iterator; - } - - inline COLONY_FORCE_INLINE iterator end() noexcept { - return end_iterator; - } - - inline COLONY_FORCE_INLINE const iterator &end() const noexcept { - return end_iterator; - } - - inline const_iterator cbegin() const noexcept { - return const_iterator( begin_iterator.group_pointer, begin_iterator.element_pointer, - begin_iterator.skipfield_pointer ); - } - - inline const_iterator cend() const noexcept { - return const_iterator( end_iterator.group_pointer, end_iterator.element_pointer, - end_iterator.skipfield_pointer ); - } - - inline reverse_iterator rbegin() - const { // May throw exception if colony is empty so end_iterator is uninitialized - return ++reverse_iterator( end_iterator ); - } - - inline reverse_iterator rend() const noexcept { - return reverse_iterator( begin_iterator.group_pointer, begin_iterator.element_pointer - 1, - begin_iterator.skipfield_pointer - 1 ); - } - - inline const_reverse_iterator crbegin() const { - return ++const_reverse_iterator( end_iterator ); - } - - inline const_reverse_iterator crend() const noexcept { - return const_reverse_iterator( begin_iterator.group_pointer, begin_iterator.element_pointer - 1, - begin_iterator.skipfield_pointer - 1 ); - } - - ~colony() noexcept { - destroy_all_data(); - } - - private: - - void destroy_all_data() noexcept { - // Amusingly enough, these changes from && to logical & actually do make a significant difference in debug mode - if( ( total_number_of_elements != 0 ) & !( std::is_trivially_destructible::value ) ) { - total_number_of_elements = 0; // to avoid double-destruction - - while( true ) { - const aligned_pointer_type end_pointer = begin_iterator.group_pointer->last_endpoint; - - do { - COLONY_DESTROY( element_allocator_type, ( *this ), - reinterpret_cast( begin_iterator.element_pointer ) ); - ++begin_iterator.skipfield_pointer; - begin_iterator.element_pointer += *begin_iterator.skipfield_pointer + 1; - begin_iterator.skipfield_pointer += *begin_iterator.skipfield_pointer; - } while( begin_iterator.element_pointer != end_pointer ); // ie. beyond end of available data - - const group_pointer_type next_group = begin_iterator.group_pointer->next_group; - COLONY_DESTROY( group_allocator_type, group_allocator_pair, begin_iterator.group_pointer ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, begin_iterator.group_pointer, 1 ); - begin_iterator.group_pointer = - next_group; // required to be before if statement in order for first_group to be NULL and avoid potential double-destruction in future - - if( next_group == nullptr ) { - return; - } - - begin_iterator.element_pointer = next_group->elements + *( next_group->skipfield ); - begin_iterator.skipfield_pointer = next_group->skipfield + *( next_group->skipfield ); - } - } else { // Avoid iteration for both empty groups and trivially-destructible types eg. POD, structs, classes with empty destructors - // Technically under a type-traits-supporting compiler total_number_of_elements could be non-zero at this point, but since begin_iterator.group_pointer would already be NULL in the case of double-destruction, it's unnecessary to zero total_number_of_elements - while( begin_iterator.group_pointer != nullptr ) { - const group_pointer_type next_group = begin_iterator.group_pointer->next_group; - COLONY_DESTROY( group_allocator_type, group_allocator_pair, begin_iterator.group_pointer ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, begin_iterator.group_pointer, 1 ); - begin_iterator.group_pointer = next_group; - } - } - } - - void initialize( const skipfield_type first_group_size ) { - begin_iterator.group_pointer = COLONY_ALLOCATE( group_allocator_type, group_allocator_pair, 1, - nullptr ); - - try { - COLONY_CONSTRUCT( group_allocator_type, group_allocator_pair, begin_iterator.group_pointer, - first_group_size ); - } catch( ... ) { - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, begin_iterator.group_pointer, 1 ); - begin_iterator.group_pointer = nullptr; - throw; - } - - end_iterator.group_pointer = begin_iterator.group_pointer; - end_iterator.element_pointer = begin_iterator.element_pointer = - begin_iterator.group_pointer->elements; - end_iterator.skipfield_pointer = begin_iterator.skipfield_pointer = - begin_iterator.group_pointer->skipfield; - total_capacity = first_group_size; - } - - public: - - /** - * Inserts the element supplied to the colony, using the object's copy-constructor. Will - * insert the element into a previously erased element slot if one exists, otherwise will - * insert to back of colony. Returns iterator to location of inserted element. - */ - iterator insert( const element_type &element ) { - if( end_iterator.element_pointer != nullptr ) { - switch( ( ( groups_with_erasures_list_head != nullptr ) << 1 ) | ( end_iterator.element_pointer == - reinterpret_cast( end_iterator.group_pointer->skipfield ) ) ) { - case 0: { // ie. there are no erased elements and end_iterator is not at end of current final group - // Make copy for return before modifying end_iterator - const iterator return_iterator = end_iterator; - - if COLONY_CONSTEXPR( std::is_nothrow_copy_constructible::value ) { - // For no good reason this compiles to faster code under GCC: - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer++ ), element ); - end_iterator.group_pointer->last_endpoint = end_iterator.element_pointer; - } else { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer ), element ); - // Shift the addition to the second operation, avoiding problems if an exception is thrown during construction - end_iterator.group_pointer->last_endpoint = ++end_iterator.element_pointer; - } - - ++( end_iterator.group_pointer->number_of_elements ); - ++end_iterator.skipfield_pointer; - ++total_number_of_elements; - - return return_iterator; // return value before incrementation - } - case 1: { // ie. there are no erased elements and end_iterator is at end of current final group - ie. colony is full - create new group - end_iterator.group_pointer->next_group = COLONY_ALLOCATE( group_allocator_type, - group_allocator_pair, 1, end_iterator.group_pointer ); - group &next_group = *( end_iterator.group_pointer->next_group ); - const skipfield_type new_group_size = ( total_number_of_elements < static_cast - ( group_allocator_pair.max_elements_per_group ) ) ? static_cast - ( total_number_of_elements ) : group_allocator_pair.max_elements_per_group; - - try { - COLONY_CONSTRUCT( group_allocator_type, group_allocator_pair, &next_group, new_group_size, - end_iterator.group_pointer ); - } catch( ... ) { - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, &next_group, 1 ); - end_iterator.group_pointer->next_group = nullptr; - throw; - } - - if COLONY_CONSTEXPR( std::is_nothrow_copy_constructible::value ) { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( next_group.elements ), element ); - } else { - try { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( next_group.elements ), element ); - } catch( ... ) { - COLONY_DESTROY( group_allocator_type, group_allocator_pair, &next_group ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, &next_group, 1 ); - end_iterator.group_pointer->next_group = nullptr; - throw; - } - } - - end_iterator.group_pointer = &next_group; - end_iterator.element_pointer = next_group.last_endpoint; - end_iterator.skipfield_pointer = next_group.skipfield + 1; - ++total_number_of_elements; - total_capacity += new_group_size; - - // returns value before incrementation - return iterator( end_iterator.group_pointer, next_group.elements, next_group.skipfield ); - } - default: { // ie. there are erased elements, reuse previous-erased element locations - iterator new_location; - new_location.group_pointer = groups_with_erasures_list_head; - new_location.element_pointer = groups_with_erasures_list_head->elements + - groups_with_erasures_list_head->free_list_head; - new_location.skipfield_pointer = groups_with_erasures_list_head->skipfield + - groups_with_erasures_list_head->free_list_head; - - // always at start of skipblock, update skipblock: - const skipfield_type prev_free_list_index = *( reinterpret_cast - ( new_location.element_pointer ) ); - - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( new_location.element_pointer ), element ); - - // Update skipblock: - const skipfield_type new_value = *( new_location.skipfield_pointer ) - 1; - - // ie. skipfield was not 1, ie. a single-node skipblock, with no additional nodes to update - if( new_value != 0 ) { - // set (new) start and (original) end of skipblock to new value: - *( new_location.skipfield_pointer + new_value ) = *( new_location.skipfield_pointer + 1 ) = - new_value; - - // transfer free list node to new start node: - ++( groups_with_erasures_list_head->free_list_head ); - - // i.e. not the tail free list node - if( prev_free_list_index != std::numeric_limits::max() ) { - *( reinterpret_cast( new_location.group_pointer->elements + - prev_free_list_index ) + 1 ) = groups_with_erasures_list_head->free_list_head; - } - - *( reinterpret_cast( new_location.element_pointer + 1 ) ) = - prev_free_list_index; - *( reinterpret_cast( new_location.element_pointer + 1 ) + 1 ) = - std::numeric_limits::max(); - } else { - groups_with_erasures_list_head->free_list_head = prev_free_list_index; - - // i.e. not the last free list node - if( prev_free_list_index != std::numeric_limits::max() ) { - *( reinterpret_cast( new_location.group_pointer->elements + - prev_free_list_index ) + 1 ) = std::numeric_limits::max(); - } else { - groups_with_erasures_list_head = groups_with_erasures_list_head->erasures_list_next_group; - } - } - - *( new_location.skipfield_pointer ) = 0; - ++( new_location.group_pointer->number_of_elements ); - - if( new_location.group_pointer == begin_iterator.group_pointer && - new_location.element_pointer < begin_iterator.element_pointer ) { - // i.e. begin_iterator was moved forwards as the result of an erasure at some point, this erased element is before the current begin, hence, set current begin iterator to this element - begin_iterator = new_location; - } - - ++total_number_of_elements; - return new_location; - } - } - } else { // i.e. newly-constructed colony, no insertions yet and no groups - initialize( pointer_allocator_pair.min_elements_per_group ); - - if COLONY_CONSTEXPR( std::is_nothrow_copy_constructible::value ) { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer++ ), element ); - } else { - try { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer++ ), element ); - } catch( ... ) { - clear(); - throw; - } - } - - ++end_iterator.skipfield_pointer; - total_number_of_elements = 1; - return begin_iterator; - } - } - - /** - * Moves the element supplied to the colony, using the object's move-constructor. Will - * insert the element in a previously erased element slot if one exists, otherwise will - * insert to back of colony. Returns iterator to location of inserted element. - * - * The move-insert function is near-identical to the regular insert function, with the - * exception of the element construction method and is_nothrow tests. - */ - iterator insert( element_type &&element ) { - if( end_iterator.element_pointer != nullptr ) { - switch( ( ( groups_with_erasures_list_head != nullptr ) << 1 ) | ( end_iterator.element_pointer == - reinterpret_cast( end_iterator.group_pointer->skipfield ) ) ) { - case 0: { - const iterator return_iterator = end_iterator; - - if COLONY_CONSTEXPR( std::is_nothrow_move_constructible::value ) { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer++ ), std::move( element ) ); - end_iterator.group_pointer->last_endpoint = end_iterator.element_pointer; - } else { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer ), std::move( element ) ); - end_iterator.group_pointer->last_endpoint = ++end_iterator.element_pointer; - } - - ++( end_iterator.group_pointer->number_of_elements ); - ++end_iterator.skipfield_pointer; - ++total_number_of_elements; - - return return_iterator; - } - case 1: { - end_iterator.group_pointer->next_group = COLONY_ALLOCATE( group_allocator_type, - group_allocator_pair, 1, end_iterator.group_pointer ); - group &next_group = *( end_iterator.group_pointer->next_group ); - const skipfield_type new_group_size = ( total_number_of_elements < static_cast - ( group_allocator_pair.max_elements_per_group ) ) ? static_cast - ( total_number_of_elements ) : group_allocator_pair.max_elements_per_group; - - try { - COLONY_CONSTRUCT( group_allocator_type, group_allocator_pair, &next_group, new_group_size, - end_iterator.group_pointer ); - } catch( ... ) { - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, &next_group, 1 ); - end_iterator.group_pointer->next_group = nullptr; - throw; - } - - if COLONY_CONSTEXPR( std::is_nothrow_move_constructible::value ) { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( next_group.elements ), std::move( element ) ); - } else { - try { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( next_group.elements ), std::move( element ) ); - } catch( ... ) { - COLONY_DESTROY( group_allocator_type, group_allocator_pair, &next_group ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, &next_group, 1 ); - end_iterator.group_pointer->next_group = nullptr; - throw; - } - } - - end_iterator.group_pointer = &next_group; - end_iterator.element_pointer = next_group.last_endpoint; - end_iterator.skipfield_pointer = next_group.skipfield + 1; - ++total_number_of_elements; - total_capacity += new_group_size; - - return iterator( end_iterator.group_pointer, next_group.elements, next_group.skipfield ); - } - default: { - iterator new_location; - new_location.group_pointer = groups_with_erasures_list_head; - new_location.element_pointer = groups_with_erasures_list_head->elements + - groups_with_erasures_list_head->free_list_head; - new_location.skipfield_pointer = groups_with_erasures_list_head->skipfield + - groups_with_erasures_list_head->free_list_head; - - const skipfield_type prev_free_list_index = *( reinterpret_cast - ( new_location.element_pointer ) ); - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( new_location.element_pointer ), std::move( element ) ); - - const skipfield_type new_value = *( new_location.skipfield_pointer ) - 1; - - if( new_value != 0 ) { - *( new_location.skipfield_pointer + new_value ) = *( new_location.skipfield_pointer + 1 ) = - new_value; - ++( groups_with_erasures_list_head->free_list_head ); - - if( prev_free_list_index != std::numeric_limits::max() ) { - *( reinterpret_cast( new_location.group_pointer->elements + - prev_free_list_index ) + 1 ) = groups_with_erasures_list_head->free_list_head; - } - - *( reinterpret_cast( new_location.element_pointer + 1 ) ) = - prev_free_list_index; - *( reinterpret_cast( new_location.element_pointer + 1 ) + 1 ) = - std::numeric_limits::max(); - } else { - groups_with_erasures_list_head->free_list_head = prev_free_list_index; - - if( prev_free_list_index != std::numeric_limits::max() ) { - *( reinterpret_cast( new_location.group_pointer->elements + - prev_free_list_index ) + 1 ) = std::numeric_limits::max(); - } else { - groups_with_erasures_list_head = groups_with_erasures_list_head->erasures_list_next_group; - } - } - - *( new_location.skipfield_pointer ) = 0; - ++( new_location.group_pointer->number_of_elements ); - - if( new_location.group_pointer == begin_iterator.group_pointer && - new_location.element_pointer < begin_iterator.element_pointer ) { - begin_iterator = new_location; - } - - ++total_number_of_elements; - - return new_location; - } - } - } else { - initialize( pointer_allocator_pair.min_elements_per_group ); - - if COLONY_CONSTEXPR( std::is_nothrow_move_constructible::value ) { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer++ ), std::move( element ) ); - } else { - try { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer++ ), std::move( element ) ); - } catch( ... ) { - clear(); - throw; - } - } - - ++end_iterator.skipfield_pointer; - total_number_of_elements = 1; - return begin_iterator; - } - } - - /** - * Constructs new element directly within colony. Will insert the element in a previously - * erased element slot if one exists, otherwise will insert to back of colony. Returns - * iterator to location of inserted element. "...parameters" are whatever parameters are - * required by the object's constructor. - * - * The emplace function is near-identical to the regular insert function, with the exception - * of the element construction method and change to is_nothrow tests. - */ - template - iterator emplace( arguments &&... parameters ) { - if( end_iterator.element_pointer != nullptr ) { - switch( ( ( groups_with_erasures_list_head != nullptr ) << 1 ) | ( end_iterator.element_pointer == - reinterpret_cast( end_iterator.group_pointer->skipfield ) ) ) { - case 0: { - const iterator return_iterator = end_iterator; - - if COLONY_CONSTEXPR( std::is_nothrow_constructible::value ) { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer++ ), - std::forward( parameters )... ); - end_iterator.group_pointer->last_endpoint = end_iterator.element_pointer; - } else { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer ), - std::forward( parameters )... ); - end_iterator.group_pointer->last_endpoint = ++end_iterator.element_pointer; - } - - ++( end_iterator.group_pointer->number_of_elements ); - ++end_iterator.skipfield_pointer; - ++total_number_of_elements; - - return return_iterator; - } - case 1: { - end_iterator.group_pointer->next_group = COLONY_ALLOCATE( group_allocator_type, - group_allocator_pair, 1, end_iterator.group_pointer ); - group &next_group = *( end_iterator.group_pointer->next_group ); - const skipfield_type new_group_size = ( total_number_of_elements < static_cast - ( group_allocator_pair.max_elements_per_group ) ) ? static_cast - ( total_number_of_elements ) : group_allocator_pair.max_elements_per_group; - - try { - COLONY_CONSTRUCT( group_allocator_type, group_allocator_pair, &next_group, new_group_size, - end_iterator.group_pointer ); - } catch( ... ) { - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, &next_group, 1 ); - end_iterator.group_pointer->next_group = nullptr; - throw; - } - - if COLONY_CONSTEXPR( std::is_nothrow_constructible::value ) { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( next_group.elements ), std::forward( parameters )... ); - } else { - try { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( next_group.elements ), std::forward( parameters )... ); - } catch( ... ) { - COLONY_DESTROY( group_allocator_type, group_allocator_pair, &next_group ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, &next_group, 1 ); - end_iterator.group_pointer->next_group = nullptr; - throw; - } - } - - end_iterator.group_pointer = &next_group; - end_iterator.element_pointer = next_group.last_endpoint; - end_iterator.skipfield_pointer = next_group.skipfield + 1; - total_capacity += new_group_size; - ++total_number_of_elements; - - return iterator( end_iterator.group_pointer, next_group.elements, next_group.skipfield ); - } - default: { - iterator new_location; - new_location.group_pointer = groups_with_erasures_list_head; - new_location.element_pointer = groups_with_erasures_list_head->elements + - groups_with_erasures_list_head->free_list_head; - new_location.skipfield_pointer = groups_with_erasures_list_head->skipfield + - groups_with_erasures_list_head->free_list_head; - - const skipfield_type prev_free_list_index = *( reinterpret_cast - ( new_location.element_pointer ) ); - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( new_location.element_pointer ), - std::forward( parameters ) ... ); - const skipfield_type new_value = *( new_location.skipfield_pointer ) - 1; - - if( new_value != 0 ) { - *( new_location.skipfield_pointer + new_value ) = *( new_location.skipfield_pointer + 1 ) = - new_value; - ++( groups_with_erasures_list_head->free_list_head ); - - if( prev_free_list_index != std::numeric_limits::max() ) { - *( reinterpret_cast( new_location.group_pointer->elements + - prev_free_list_index ) + 1 ) = groups_with_erasures_list_head->free_list_head; - } - - *( reinterpret_cast( new_location.element_pointer + 1 ) ) = - prev_free_list_index; - *( reinterpret_cast( new_location.element_pointer + 1 ) + 1 ) = - std::numeric_limits::max(); - } else { - groups_with_erasures_list_head->free_list_head = prev_free_list_index; - - if( prev_free_list_index != std::numeric_limits::max() ) { - *( reinterpret_cast( new_location.group_pointer->elements + - prev_free_list_index ) + 1 ) = std::numeric_limits::max(); - } else { - groups_with_erasures_list_head = groups_with_erasures_list_head->erasures_list_next_group; - } - } - - *( new_location.skipfield_pointer ) = 0; - ++( new_location.group_pointer->number_of_elements ); - - if( new_location.group_pointer == begin_iterator.group_pointer && - new_location.element_pointer < begin_iterator.element_pointer ) { - begin_iterator = new_location; - } - - ++total_number_of_elements; - - return new_location; - } - } - } else { - initialize( pointer_allocator_pair.min_elements_per_group ); - - if COLONY_CONSTEXPR( std::is_nothrow_constructible::value ) { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer++ ), - std::forward( parameters ) ... ); - } else { - try { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer++ ), - std::forward( parameters ) ... ); - } catch( ... ) { - clear(); - throw; - } - } - - ++end_iterator.skipfield_pointer; - total_number_of_elements = 1; - return begin_iterator; - } - } - - private: - - // Internal functions for fill insert: - void group_create( const skipfield_type number_of_elements ) { - const group_pointer_type next_group = end_iterator.group_pointer->next_group = COLONY_ALLOCATE( - group_allocator_type, group_allocator_pair, 1, end_iterator.group_pointer ); - - try { - COLONY_CONSTRUCT( group_allocator_type, group_allocator_pair, next_group, number_of_elements, - end_iterator.group_pointer ); - } catch( ... ) { - COLONY_DESTROY( group_allocator_type, group_allocator_pair, next_group ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, next_group, 1 ); - end_iterator.group_pointer->next_group = nullptr; - throw; - } - - end_iterator.group_pointer = next_group; - end_iterator.element_pointer = next_group->elements; - // group constructor sets this to 1 by default to allow for faster insertion during insertion/emplace in other cases - next_group->number_of_elements = 0; - total_capacity += number_of_elements; - } - - void group_fill( const element_type &element, const skipfield_type number_of_elements ) { - // ie. we can get away with using the cheaper fill_n here if there is no chance of an exception being thrown: - if COLONY_CONSTEXPR( std::is_trivially_copyable::value && - std::is_trivially_copy_constructible::value && - std::is_nothrow_copy_constructible::value ) { - if COLONY_CONSTEXPR( sizeof( aligned_element_type ) == sizeof( element_type ) ) { - std::fill_n( reinterpret_cast( end_iterator.element_pointer ), number_of_elements, - element ); - } else { - // to avoid potentially violating memory boundaries in line below, create an initial copy object of same (but aligned) type - alignas( sizeof( aligned_element_type ) ) element_type aligned_copy = element; - std::fill_n( end_iterator.element_pointer, number_of_elements, - *( reinterpret_cast( &aligned_copy ) ) ); - } - - end_iterator.element_pointer += number_of_elements; - } else { - const aligned_pointer_type fill_end = end_iterator.element_pointer + number_of_elements; - - do { - try { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), - reinterpret_cast( end_iterator.element_pointer++ ), element ); - } catch( ... ) { - end_iterator.group_pointer->last_endpoint = --end_iterator.element_pointer; - const skipfield_type elements_constructed_before_exception = static_cast - ( end_iterator.element_pointer - end_iterator.group_pointer->elements ); - end_iterator.group_pointer->number_of_elements = elements_constructed_before_exception; - end_iterator.skipfield_pointer = end_iterator.group_pointer->skipfield + - elements_constructed_before_exception; - throw; - } - } while( end_iterator.element_pointer != fill_end ); - } - - end_iterator.group_pointer->last_endpoint = end_iterator.element_pointer; - end_iterator.group_pointer->number_of_elements += number_of_elements; - } - - void fill_skipblock( const element_type &element, aligned_pointer_type const location, - skipfield_pointer_type const skipfield_pointer, const skipfield_type number_of_elements ) { - // ie. we can get away with using the cheaper fill_n here if there is no chance of an exception being thrown: - if COLONY_CONSTEXPR( std::is_trivially_copyable::value && - std::is_trivially_copy_constructible::value && - std::is_nothrow_copy_constructible::value ) { - if COLONY_CONSTEXPR( sizeof( aligned_element_type ) == sizeof( element_type ) ) { - std::fill_n( reinterpret_cast( location ), number_of_elements, element ); - } else { - // to avoid potentially violating memory boundaries in line below, create an initial copy object of same (but aligned) type - alignas( sizeof( aligned_element_type ) ) element_type aligned_copy = element; - std::fill_n( location, number_of_elements, - *( reinterpret_cast( &aligned_copy ) ) ); - } - } else { - // in case of exception, grabbing indexes before free_list node is reused - const skipfield_type prev_free_list_node = *( reinterpret_cast - ( location ) ); - const aligned_pointer_type fill_end = location + number_of_elements; - - for( aligned_pointer_type current_location = location; current_location != fill_end; - ++current_location ) { - try { - COLONY_CONSTRUCT( element_allocator_type, ( *this ), reinterpret_cast( current_location ), - element ); - } catch( ... ) { - // Reconstruct existing skipblock and free-list indexes to reflect partially-reused skipblock: - const skipfield_type elements_constructed_before_exception = static_cast( ( - current_location - 1 ) - location ); - groups_with_erasures_list_head->number_of_elements += elements_constructed_before_exception; - total_number_of_elements += elements_constructed_before_exception; - - std::memset( skipfield_pointer, 0, - elements_constructed_before_exception * sizeof( skipfield_type ) ); - - *( reinterpret_cast( location + elements_constructed_before_exception ) ) = - prev_free_list_node; - *( reinterpret_cast( location + elements_constructed_before_exception ) + - 1 ) = std::numeric_limits::max(); - - const skipfield_type new_skipblock_head_index = static_cast - ( location - groups_with_erasures_list_head->elements ) + elements_constructed_before_exception; - groups_with_erasures_list_head->free_list_head = new_skipblock_head_index; - - if( prev_free_list_node != std::numeric_limits::max() ) { - *( reinterpret_cast( groups_with_erasures_list_head->elements + - prev_free_list_node ) + 1 ) = new_skipblock_head_index; - } - - throw; - } - } - } - - // reset skipfield nodes within skipblock to 0 - std::memset( skipfield_pointer, 0, number_of_elements * sizeof( skipfield_type ) ); - groups_with_erasures_list_head->number_of_elements += number_of_elements; - total_number_of_elements += number_of_elements; - } - - public: - - /** - * Fill insert: - * Inserts n copies of val into the colony. Will insert the element into a previously erased - * element slot if one exists, otherwise will insert to back of colony. - */ - void insert( size_type number_of_elements, const element_type &element ) { - if( number_of_elements == 0 ) { - return; - } else if( number_of_elements == 1 ) { - insert( element ); - return; - } - - if( begin_iterator.group_pointer == nullptr ) { // Empty colony, no groups created yet - initialize( ( number_of_elements > group_allocator_pair.max_elements_per_group ) ? - group_allocator_pair.max_elements_per_group : ( number_of_elements < - pointer_allocator_pair.min_elements_per_group ) ? pointer_allocator_pair.min_elements_per_group : - static_cast( number_of_elements ) ); // Construct first group - begin_iterator.group_pointer->number_of_elements = 0; - } - - // ie. not an uninitialized colony or a situation where reserve has been called - if( total_number_of_elements != 0 ) { - // Use up erased locations if available: - if( groups_with_erasures_list_head != nullptr ) { - do { // skipblock loop: breaks when group is exhausted of reusable skipblocks, or returns if number_of_elements == 0 - aligned_pointer_type const element_pointer = groups_with_erasures_list_head->elements + - groups_with_erasures_list_head->free_list_head; - skipfield_pointer_type const skipfield_pointer = groups_with_erasures_list_head->skipfield + - groups_with_erasures_list_head->free_list_head; - const skipfield_type skipblock_size = *skipfield_pointer; - - if( groups_with_erasures_list_head == begin_iterator.group_pointer && - element_pointer < begin_iterator.element_pointer ) { - begin_iterator.element_pointer = element_pointer; - begin_iterator.skipfield_pointer = skipfield_pointer; - } - - if( skipblock_size <= number_of_elements ) { - // set free list head to previous free list node - groups_with_erasures_list_head->free_list_head = *( reinterpret_cast - ( element_pointer ) ); - fill_skipblock( element, element_pointer, skipfield_pointer, skipblock_size ); - number_of_elements -= skipblock_size; - - if( groups_with_erasures_list_head->free_list_head != std::numeric_limits::max() ) { - // set 'next' index of new free list head to 'end' (numeric max) - *( reinterpret_cast( groups_with_erasures_list_head->elements + - groups_with_erasures_list_head->free_list_head ) + 1 ) = std::numeric_limits::max(); - } else { - // change groups - groups_with_erasures_list_head = groups_with_erasures_list_head->erasures_list_next_group; - - if( groups_with_erasures_list_head == nullptr ) { - break; - } - } - } else { // skipblock is larger than remaining number of elements - // save before element location is overwritten - const skipfield_type prev_index = *( reinterpret_cast( element_pointer ) ); - fill_skipblock( element, element_pointer, skipfield_pointer, - static_cast( number_of_elements ) ); - const skipfield_type new_skipblock_size = static_cast( skipblock_size - - number_of_elements ); - - // Update skipfield (earlier nodes already memset'd in fill_skipblock function): - *( skipfield_pointer + number_of_elements ) = new_skipblock_size; - *( skipfield_pointer + skipblock_size - 1 ) = new_skipblock_size; - // set free list head to new start node - groups_with_erasures_list_head->free_list_head += static_cast( number_of_elements ); - - // Update free list with new head: - *( reinterpret_cast( element_pointer + number_of_elements ) ) = prev_index; - *( reinterpret_cast( element_pointer + number_of_elements ) + 1 ) = - std::numeric_limits::max(); - - if( prev_index != std::numeric_limits::max() ) { - // set 'next' index of previous skipblock to new start of skipblock - *( reinterpret_cast( groups_with_erasures_list_head->elements + prev_index ) - + 1 ) = groups_with_erasures_list_head->free_list_head; - } - - return; - } - } while( number_of_elements != 0 ); - } - - // Use up remaining available element locations in end group: - const skipfield_type group_remainder = ( static_cast - ( reinterpret_cast( end_iterator.group_pointer->skipfield ) - - end_iterator.element_pointer ) > number_of_elements ) ? static_cast - ( number_of_elements ) : static_cast( reinterpret_cast - ( end_iterator.group_pointer->skipfield ) - end_iterator.element_pointer ); - - if( group_remainder != 0 ) { - group_fill( element, group_remainder ); - total_number_of_elements += group_remainder; - number_of_elements -= group_remainder; - } - } else if( end_iterator.group_pointer->capacity >= number_of_elements ) { - group_fill( element, static_cast( number_of_elements ) ); - end_iterator.skipfield_pointer = end_iterator.group_pointer->skipfield + number_of_elements; - total_number_of_elements = number_of_elements; - return; - } else { - group_fill( element, end_iterator.group_pointer->capacity ); - total_number_of_elements = end_iterator.group_pointer->capacity; - number_of_elements -= end_iterator.group_pointer->capacity; - } - - // If there's some elements left that need to be created, create new groups and fill: - if( number_of_elements > group_allocator_pair.max_elements_per_group ) { - size_type multiples = ( number_of_elements / static_cast - ( group_allocator_pair.max_elements_per_group ) ); - const skipfield_type element_remainder = static_cast( number_of_elements - - ( multiples * static_cast( group_allocator_pair.max_elements_per_group ) ) ); - - while( multiples-- != 0 ) { - group_create( group_allocator_pair.max_elements_per_group ); - group_fill( element, group_allocator_pair.max_elements_per_group ); - } - - if( element_remainder != 0 ) { - group_create( group_allocator_pair.max_elements_per_group ); - group_fill( element, element_remainder ); - } - } else if( number_of_elements != 0 ) { - group_create( static_cast( ( number_of_elements > total_number_of_elements ) ? - number_of_elements : total_number_of_elements ) ); - group_fill( element, static_cast( number_of_elements ) ); - } - - // Adds the remainder from the last if-block - the insert functions in the first if/else block will already have incremented total_number_of_elements - total_number_of_elements += number_of_elements; - end_iterator.skipfield_pointer = end_iterator.group_pointer->skipfield + - ( end_iterator.element_pointer - end_iterator.group_pointer->elements ); - } - - /** - * Range insert: - * Inserts a series of value_type elements from an external source into a colony holding the - * same value_type (eg. int, float, a particular class, etcetera). Stops inserting once it - * reaches last. - */ - template - inline void insert( typename enable_if_c < !std::numeric_limits::is_integer, - iterator_type >::type first, const iterator_type last ) { - while( first != last ) { - insert( *first++ ); - } - } - - /** - * Initializer-list insert: - * Copies elements from an initializer list into the colony. Will insert the element in a - * previously erased element slot if one exists, otherwise will insert to back of colony. - */ - inline void insert( const std::initializer_list &element_list ) { - // use range insert: - insert( element_list.begin(), element_list.end() ); - } - - private: - - inline COLONY_FORCE_INLINE void update_subsequent_group_numbers( group_pointer_type current_group ) - noexcept { - do { - --( current_group->group_number ); - current_group = current_group->next_group; - } while( current_group != nullptr ); - } - - // get all elements contiguous in memory and shrink to fit, remove erasures and erasure free lists - inline COLONY_FORCE_INLINE void consolidate() { - colony temp; - // Make first allocated group as large total number of elements, where possible - temp.change_group_sizes( static_cast( ( - pointer_allocator_pair.min_elements_per_group > total_number_of_elements ) ? - pointer_allocator_pair.min_elements_per_group : ( ( total_number_of_elements > - group_allocator_pair.max_elements_per_group ) ? group_allocator_pair.max_elements_per_group : - total_number_of_elements ) ), - group_allocator_pair.max_elements_per_group ); - - if COLONY_CONSTEXPR( std::is_move_assignable::value && - std::is_move_constructible::value ) { - temp.insert( std::make_move_iterator( begin_iterator ), std::make_move_iterator( end_iterator ) ); - } else { - temp.insert( begin_iterator, end_iterator ); - } - // reset to correct value for future clear() or erasures - temp.pointer_allocator_pair.min_elements_per_group = pointer_allocator_pair.min_elements_per_group; - // Avoid generating 2nd temporary - *this = std::move( temp ); - } - - void remove_from_groups_with_erasures_list( const group_pointer_type group_to_remove ) noexcept { - if( group_to_remove == groups_with_erasures_list_head ) { - groups_with_erasures_list_head = groups_with_erasures_list_head->erasures_list_next_group; - return; - } - - group_pointer_type previous_group = groups_with_erasures_list_head; - group_pointer_type current_group = groups_with_erasures_list_head->erasures_list_next_group; - - while( group_to_remove != current_group ) { - previous_group = current_group; - current_group = current_group->erasures_list_next_group; - } - - previous_group->erasures_list_next_group = current_group->erasures_list_next_group; - } - - public: - - /** - * Removes the element pointed to by the supplied iterator, from the colony. Returns an - * iterator pointing to the next non-erased element in the colony (or to end() if no more - * elements are available). This must return an iterator because if a colony group becomes - * entirely empty, it will be removed from the colony, invalidating the existing iterator. - * Attempting to erase a previously-erased element results in undefined behavior (this is - * checked for via an assert in debug mode). - * - * must return iterator to subsequent non-erased element (or end()), in case the group - * containing the element which the iterator points to becomes empty after the erasure, and - * is thereafter removed from the colony chain, making the current iterator invalid and - * unusable in a ++ operation: - * - * if uninitialized/invalid iterator supplied, function could generate an exception - */ - iterator erase( const const_iterator &it ) { - assert( !empty() ); - const group_pointer_type group_pointer = it.group_pointer; - // not uninitialized iterator - assert( group_pointer != nullptr ); - // != end() - assert( it.element_pointer != group_pointer->last_endpoint ); - // element pointed to by iterator has not been erased previously - assert( *( it.skipfield_pointer ) == 0 ); - - // This if-statement should be removed by the compiler on resolution of element_type. For some optimizing compilers this step won't be necessary (for MSVC 2013 it makes a difference) - if COLONY_CONSTEXPR( !( std::is_trivially_destructible::value ) ) { - COLONY_DESTROY( element_allocator_type, ( *this ), - reinterpret_cast( it.element_pointer ) ); // Destruct element - } - - --total_number_of_elements; - - // ie. non-empty group at this point in time, don't consolidate - optimization note: GCC optimizes postfix + 1 comparison better than prefix + 1 comparison in many cases. - if( group_pointer->number_of_elements-- != 1 ) { - // Code logic for following section: - // --------------------------------- - // If current skipfield node has no skipped node on either side, continue as usual - // If node only has skipped node on left, set current node and start node of the skipblock to left node value + 1. - // If node only has skipped node on right, make this node the start node of the skipblock and update end node - // If node has skipped nodes on left and right, set start node of left skipblock and end node of right skipblock to the values of the left + right nodes + 1 - - // Optimization explanation: - // The contextual logic below is the same as that in the insert() functions but in this case the value of the current skipfield node will always be - // zero (since it is not yet erased), meaning no additional manipulations are necessary for the previous skipfield node comparison - we only have to check against zero - const char prev_skipfield = *( it.skipfield_pointer - ( it.skipfield_pointer != - group_pointer->skipfield ) ) != 0; - // NOTE: boundary test (checking against end-of-elements) is able to be skipped due to the extra skipfield node (compared to element field) - which is present to enable faster iterator operator ++ operations - const char after_skipfield = *( it.skipfield_pointer + 1 ) != 0; - skipfield_type update_value = 1; - - switch( ( after_skipfield << 1 ) | prev_skipfield ) { - case 0: { // no consecutive erased elements - *it.skipfield_pointer = 1; // solo skipped node - const skipfield_type index = static_cast( it.element_pointer - - group_pointer->elements ); - - // ie. if this group already has some erased elements - if( group_pointer->free_list_head != std::numeric_limits::max() ) { - // set prev free list head's 'next index' number to the index of the current element - *( reinterpret_cast( group_pointer->elements + - group_pointer->free_list_head ) + 1 ) = index; - } else { - // add it to the groups-with-erasures free list - group_pointer->erasures_list_next_group = groups_with_erasures_list_head; - groups_with_erasures_list_head = group_pointer; - } - - *( reinterpret_cast( it.element_pointer ) ) = group_pointer->free_list_head; - *( reinterpret_cast( it.element_pointer ) + 1 ) = - std::numeric_limits::max(); - group_pointer->free_list_head = index; - break; - } - case 1: { // previous erased consecutive elements, none following - *( it.skipfield_pointer - * ( it.skipfield_pointer - 1 ) ) = *it.skipfield_pointer = * - ( it.skipfield_pointer - 1 ) + 1; - break; - } - case 2: { // following erased consecutive elements, none preceding - const skipfield_type following_value = *( it.skipfield_pointer + 1 ) + 1; - *( it.skipfield_pointer + following_value - 1 ) = *( it.skipfield_pointer ) = following_value; - - const skipfield_type following_previous = *( reinterpret_cast - ( it.element_pointer + 1 ) ); - const skipfield_type following_next = *( reinterpret_cast - ( it.element_pointer + 1 ) + 1 ); - *( reinterpret_cast( it.element_pointer ) ) = following_previous; - *( reinterpret_cast( it.element_pointer ) + 1 ) = following_next; - - const skipfield_type index = static_cast( it.element_pointer - - group_pointer->elements ); - - if( following_previous != std::numeric_limits::max() ) { - // Set next index of previous free list node to this node's 'next' index - *( reinterpret_cast( group_pointer->elements + following_previous ) + 1 ) = - index; - } - - if( following_next != std::numeric_limits::max() ) { - // Set previous index of next free list node to this node's 'previous' index - *( reinterpret_cast( group_pointer->elements + following_next ) ) = index; - } else { - group_pointer->free_list_head = index; - } - - update_value = following_value; - break; - } - case 3: { // both preceding and following consecutive erased elements - const skipfield_type preceding_value = *( it.skipfield_pointer - 1 ); - const skipfield_type following_value = *( it.skipfield_pointer + 1 ) + 1; - - // Join the skipblocks - *( it.skipfield_pointer - preceding_value ) = *( it.skipfield_pointer + following_value - 1 ) = - preceding_value + following_value; - - // Remove the following skipblock's entry from the free list - const skipfield_type following_previous = *( reinterpret_cast - ( it.element_pointer + 1 ) ); - const skipfield_type following_next = *( reinterpret_cast - ( it.element_pointer + 1 ) + 1 ); - - if( following_previous != std::numeric_limits::max() ) { - // Set next index of previous free list node to this node's 'next' index - *( reinterpret_cast( group_pointer->elements + following_previous ) + 1 ) = - following_next; - } - - if( following_next != std::numeric_limits::max() ) { - // Set previous index of next free list node to this node's 'previous' index - *( reinterpret_cast( group_pointer->elements + following_next ) ) = - following_previous; - } else { - group_pointer->free_list_head = following_previous; - } - - update_value = following_value; - break; - } - } - - iterator return_iterator( it.group_pointer, it.element_pointer + update_value, - it.skipfield_pointer + update_value ); - return_iterator.check_for_end_of_group_and_progress(); - - // If original iterator was first element in colony, update it's value with the next non-erased element: - if( it.element_pointer == begin_iterator.element_pointer ) { - begin_iterator = return_iterator; - } - - return return_iterator; - } - - // else: group is empty, consolidate groups - switch( ( group_pointer->next_group != nullptr ) | ( ( group_pointer != - begin_iterator.group_pointer ) - << 1 ) ) { - case 0: { // ie. group_pointer == begin_iterator.group_pointer && group_pointer->next_group == NULL; only group in colony - // Reset skipfield and free list rather than clearing - leads to fewer allocations/deallocations: - // &* to avoid problems with non-trivial pointers. Although there is one more skipfield than group_pointer->capacity, capacity + 1 is not necessary here as the end skipfield is never written to after initialization - std::memset( &*( group_pointer->skipfield ), 0, - sizeof( skipfield_type ) * group_pointer->capacity ); - group_pointer->free_list_head = std::numeric_limits::max(); - groups_with_erasures_list_head = nullptr; - - // Reset begin and end iterators: - end_iterator.element_pointer = begin_iterator.element_pointer = group_pointer->last_endpoint = - group_pointer->elements; - end_iterator.skipfield_pointer = begin_iterator.skipfield_pointer = group_pointer->skipfield; - - return end_iterator; - } - case 1: { // ie. group_pointer == begin_iterator.group_pointer && group_pointer->next_group != NULL. Remove first group, change first group to next group - group_pointer->next_group->previous_group = nullptr; // Cut off this group from the chain - begin_iterator.group_pointer = group_pointer->next_group; // Make the next group the first group - - update_subsequent_group_numbers( begin_iterator.group_pointer ); - - // Erasures present within the group, ie. was part of the intrusive list of groups with erasures. - if( group_pointer->free_list_head != std::numeric_limits::max() ) { - remove_from_groups_with_erasures_list( group_pointer ); - } - - total_capacity -= group_pointer->capacity; - COLONY_DESTROY( group_allocator_type, group_allocator_pair, group_pointer ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, group_pointer, 1 ); - - // note: end iterator only needs to be changed if the deleted group was the final group in the chain ie. not in this case - // If the beginning index has been erased (ie. skipfield != 0), skip to next non-erased element - begin_iterator.element_pointer = begin_iterator.group_pointer->elements + * - ( begin_iterator.group_pointer->skipfield ); - begin_iterator.skipfield_pointer = begin_iterator.group_pointer->skipfield + * - ( begin_iterator.group_pointer->skipfield ); - - return begin_iterator; - } - case 3: { // this is a non-first group but not final group in chain: delete the group, then link previous group to the next group in the chain: - group_pointer->next_group->previous_group = group_pointer->previous_group; - // close the chain, removing this group from it - const group_pointer_type return_group = group_pointer->previous_group->next_group = - group_pointer->next_group; - - update_subsequent_group_numbers( return_group ); - - if( group_pointer->free_list_head != std::numeric_limits::max() ) { - remove_from_groups_with_erasures_list( group_pointer ); - } - - total_capacity -= group_pointer->capacity; - COLONY_DESTROY( group_allocator_type, group_allocator_pair, group_pointer ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, group_pointer, 1 ); - - // Return next group's first non-erased element: - return iterator( return_group, return_group->elements + * ( return_group->skipfield ), - return_group->skipfield + * ( return_group->skipfield ) ); - } - default: { // this is a non-first group and the final group in the chain - if( group_pointer->free_list_head != std::numeric_limits::max() ) { - remove_from_groups_with_erasures_list( group_pointer ); - } - - group_pointer->previous_group->next_group = nullptr; - // end iterator needs to be changed as element supplied was the back element of the colony - end_iterator.group_pointer = group_pointer->previous_group; - end_iterator.element_pointer = reinterpret_cast - ( end_iterator.group_pointer->skipfield ); - end_iterator.skipfield_pointer = end_iterator.group_pointer->skipfield + - end_iterator.group_pointer->capacity; - - total_capacity -= group_pointer->capacity; - COLONY_DESTROY( group_allocator_type, group_allocator_pair, group_pointer ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, group_pointer, 1 ); - - return end_iterator; - } - } - } - - /** - * Range erase: - * Erases all elements of a given colony from first to the element before the last iterator. - * This function is optimized for multiple consecutive erasures and will always be faster - * than sequential single-element erase calls in that scenario. - */ - void erase( const const_iterator &iterator1, const const_iterator &iterator2 ) { - // if uninitialized/invalid iterators supplied, function could generate an exception. If iterator1 > iterator2, behavior is undefined. - assert( iterator1 <= iterator2 ); - - iterator current = iterator1; - - if( current.group_pointer != iterator2.group_pointer ) { - if( current.element_pointer != current.group_pointer->elements + * - ( current.group_pointer->skipfield ) ) { - // if iterator1 is not the first non-erased element in it's group - most common case - size_type number_of_group_erasures = 0; - - // Now update skipfield: - const aligned_pointer_type end = iterator1.group_pointer->last_endpoint; - - // Schema: first erase all non-erased elements until end of group & remove all skipblocks post-iterator1 from the free_list. Then, either update preceding skipblock or create new one: - - // if trivially-destructible, and no erasures in group, skip while loop below and just jump straight to the location - if( ( std::is_trivially_destructible::value ) & - ( current.group_pointer->free_list_head == std::numeric_limits::max() ) ) { - number_of_group_erasures += static_cast( end - current.element_pointer ); - } else { - while( current.element_pointer != end ) { - if( *current.skipfield_pointer == 0 ) { - if COLONY_CONSTEXPR( !( std::is_trivially_destructible::value ) ) { - COLONY_DESTROY( element_allocator_type, ( *this ), - reinterpret_cast( current.element_pointer ) ); // Destruct element - } - - ++number_of_group_erasures; - ++current.element_pointer; - ++current.skipfield_pointer; - } else { // remove skipblock from group: - const skipfield_type prev_free_list_index = *( reinterpret_cast - ( current.element_pointer ) ); - const skipfield_type next_free_list_index = *( reinterpret_cast - ( current.element_pointer ) + 1 ); - - current.element_pointer += *( current.skipfield_pointer ); - current.skipfield_pointer += *( current.skipfield_pointer ); - - // if this is the last skipblock in the free list - if( next_free_list_index == std::numeric_limits::max() && - prev_free_list_index == std::numeric_limits::max() ) { - // remove group from list of free-list groups - will be added back in down below, but not worth optimizing for - remove_from_groups_with_erasures_list( iterator1.group_pointer ); - iterator1.group_pointer->free_list_head = std::numeric_limits::max(); - number_of_group_erasures += static_cast( end - current.element_pointer ); - - if COLONY_CONSTEXPR( !( std::is_trivially_destructible::value ) ) { - // miniloop - avoid checking skipfield for rest of elements in group, as there are no more skipped elements now - while( current.element_pointer != end ) { - COLONY_DESTROY( element_allocator_type, ( *this ), - reinterpret_cast( current.element_pointer++ ) ); // Destruct element - } - } - - break; // end overall while loop - } else if( next_free_list_index == std::numeric_limits::max() ) { - // if this is the head of the free list - // make free list head equal to next free list node - current.group_pointer->free_list_head = prev_free_list_index; - *( reinterpret_cast( current.group_pointer->elements + - prev_free_list_index ) + 1 ) = std::numeric_limits::max(); - } else { // either a tail or middle free list node - *( reinterpret_cast( current.group_pointer->elements + - next_free_list_index ) ) = prev_free_list_index; - - if( prev_free_list_index != - std::numeric_limits::max() ) { // ie. not the tail free list node - *( reinterpret_cast( current.group_pointer->elements + - prev_free_list_index ) + 1 ) = next_free_list_index; - } - } - } - } - } - - const skipfield_type previous_node_value = *( iterator1.skipfield_pointer - 1 ); - const skipfield_type distance_to_end = static_cast( end - - iterator1.element_pointer ); - - if( previous_node_value == 0 ) { // no previous skipblock - *iterator1.skipfield_pointer = distance_to_end; - *( iterator1.skipfield_pointer + distance_to_end - 1 ) = distance_to_end; - - const skipfield_type index = static_cast( iterator1.element_pointer - - iterator1.group_pointer->elements ); - - if( iterator1.group_pointer->free_list_head != std::numeric_limits::max() ) { - // if this group already has some erased elements - // set prev free list head's 'next index' number to the index of the iterator1 element - *( reinterpret_cast( iterator1.group_pointer->elements + - iterator1.group_pointer->free_list_head ) + 1 ) = index; - } else { - // add it to the groups-with-erasures free list - iterator1.group_pointer->erasures_list_next_group = groups_with_erasures_list_head; - groups_with_erasures_list_head = iterator1.group_pointer; - } - - *( reinterpret_cast( iterator1.element_pointer ) ) = - iterator1.group_pointer->free_list_head; - *( reinterpret_cast( iterator1.element_pointer ) + 1 ) = - std::numeric_limits::max(); - iterator1.group_pointer->free_list_head = index; - } else { - // update previous skipblock, no need to update free list: - *( iterator1.skipfield_pointer - previous_node_value ) = *( iterator1.skipfield_pointer + - distance_to_end - 1 ) = previous_node_value + distance_to_end; - } - - iterator1.group_pointer->number_of_elements -= static_cast - ( number_of_group_erasures ); - total_number_of_elements -= number_of_group_erasures; - - current.group_pointer = current.group_pointer->next_group; - } - - // Intermediate groups: - const group_pointer_type previous_group = current.group_pointer->previous_group; - - while( current.group_pointer != iterator2.group_pointer ) { - if COLONY_CONSTEXPR( !( std::is_trivially_destructible::value ) ) { - current.element_pointer = current.group_pointer->elements + *( current.group_pointer->skipfield ); - current.skipfield_pointer = current.group_pointer->skipfield + * - ( current.group_pointer->skipfield ); - const aligned_pointer_type end = current.group_pointer->last_endpoint; - - do { - COLONY_DESTROY( element_allocator_type, ( *this ), - reinterpret_cast( current.element_pointer ) ); // Destruct element - ++current.skipfield_pointer; - current.element_pointer += *current.skipfield_pointer + 1; - current.skipfield_pointer += *current.skipfield_pointer; - } while( current.element_pointer != end ); - } - - if( current.group_pointer->free_list_head != std::numeric_limits::max() ) { - remove_from_groups_with_erasures_list( current.group_pointer ); - } - - total_number_of_elements -= current.group_pointer->number_of_elements; - const group_pointer_type current_group = current.group_pointer; - current.group_pointer = current.group_pointer->next_group; - - total_capacity -= current_group->capacity; - COLONY_DESTROY( group_allocator_type, group_allocator_pair, current_group ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, current_group, 1 ); - } - - current.element_pointer = current.group_pointer->elements + *( current.group_pointer->skipfield ); - current.skipfield_pointer = current.group_pointer->skipfield + * - ( current.group_pointer->skipfield ); - current.group_pointer->previous_group = previous_group; - - if( previous_group != nullptr ) { - previous_group->next_group = current.group_pointer; - } else { - // This line is included here primarily to avoid a secondary if statement within the if block below - it will not be needed in any other situation - begin_iterator = iterator2; - } - } - - // in case iterator2 was at beginning of it's group - also covers empty range case (first == last) - if( current.element_pointer == iterator2.element_pointer ) { - return; - } - - // Final group: - // Code explanation: - // If not erasing entire final group, 1. Destruct elements (if non-trivial destructor) and add locations to group free list. 2. process skipfield. - // If erasing entire group, 1. Destruct elements (if non-trivial destructor), 2. if no elements left in colony, clear() 3. otherwise reset end_iterator and remove group from groups-with-erasures list (if free list of erasures present) - if( iterator2.element_pointer != end_iterator.element_pointer || - current.element_pointer != current.group_pointer->elements + * - ( current.group_pointer->skipfield ) ) { // ie. not erasing entire group - size_type number_of_group_erasures = 0; - // Schema: first erased all non-erased elements until end of group & remove all skipblocks post-iterator2 from the free_list. Then, either update preceding skipblock or create new one: - - const iterator current_saved = current; - - // if trivially-destructible, and no erasures in group, skip while loop below and just jump straight to the location - if( ( std::is_trivially_destructible::value ) & - ( current.group_pointer->free_list_head == std::numeric_limits::max() ) ) { - number_of_group_erasures += static_cast( iterator2.element_pointer - - current.element_pointer ); - } else { - while( current.element_pointer != iterator2.element_pointer ) { - if( *current.skipfield_pointer == 0 ) { - if COLONY_CONSTEXPR( !( std::is_trivially_destructible::value ) ) { - COLONY_DESTROY( element_allocator_type, ( *this ), - reinterpret_cast( current.element_pointer ) ); // Destruct element - } - - ++number_of_group_erasures; - ++current.element_pointer; - ++current.skipfield_pointer; - } else { // remove skipblock from group: - const skipfield_type prev_free_list_index = *( reinterpret_cast - ( current.element_pointer ) ); - const skipfield_type next_free_list_index = *( reinterpret_cast - ( current.element_pointer ) + 1 ); - - current.element_pointer += *( current.skipfield_pointer ); - current.skipfield_pointer += *( current.skipfield_pointer ); - - if( next_free_list_index == std::numeric_limits::max() && - prev_free_list_index == std::numeric_limits::max() ) { - // if this is the last skipblock in the free list - // remove group from list of free-list groups - will be added back in down below, but not worth optimizing for - remove_from_groups_with_erasures_list( iterator2.group_pointer ); - iterator2.group_pointer->free_list_head = std::numeric_limits::max(); - number_of_group_erasures += static_cast( iterator2.element_pointer - - current.element_pointer ); - - if COLONY_CONSTEXPR( !( std::is_trivially_destructible::value ) ) { - while( current.element_pointer != iterator2.element_pointer ) { - COLONY_DESTROY( element_allocator_type, ( *this ), - reinterpret_cast( current.element_pointer++ ) ); // Destruct element - } - } - - break; // end overall while loop - } else if( next_free_list_index == - std::numeric_limits::max() ) { // if this is the head of the free list - current.group_pointer->free_list_head = prev_free_list_index; - *( reinterpret_cast( current.group_pointer->elements + - prev_free_list_index ) + 1 ) = std::numeric_limits::max(); - } else { - *( reinterpret_cast( current.group_pointer->elements + - next_free_list_index ) ) = prev_free_list_index; - - if( prev_free_list_index != - std::numeric_limits::max() ) { // ie. not the tail free list node - *( reinterpret_cast( current.group_pointer->elements + - prev_free_list_index ) + 1 ) = next_free_list_index; - } - } - } - } - } - - const skipfield_type distance_to_iterator2 = static_cast - ( iterator2.element_pointer - current_saved.element_pointer ); - const skipfield_type index = static_cast( current_saved.element_pointer - - iterator2.group_pointer->elements ); - - if( index == 0 || *( current_saved.skipfield_pointer - 1 ) == 0 ) { - // element is either at start of group or previous skipfield node is 0 - *( current_saved.skipfield_pointer ) = distance_to_iterator2; - *( iterator2.skipfield_pointer - 1 ) = distance_to_iterator2; - - if( iterator2.group_pointer->free_list_head != std::numeric_limits::max() ) { - // if this group already has some erased elements - *( reinterpret_cast( iterator2.group_pointer->elements + - iterator2.group_pointer->free_list_head ) + 1 ) = index; - } else { - // add it to the groups-with-erasures free list - iterator2.group_pointer->erasures_list_next_group = groups_with_erasures_list_head; - groups_with_erasures_list_head = iterator2.group_pointer; - } - - *( reinterpret_cast( current_saved.element_pointer ) ) = - iterator2.group_pointer->free_list_head; - *( reinterpret_cast( current_saved.element_pointer ) + 1 ) = - std::numeric_limits::max(); - iterator2.group_pointer->free_list_head = index; - } else { // If iterator 1 & 2 are in same group, but iterator 1 was not at start of group, and previous skipfield node is an end node in a skipblock: - // Just update existing skipblock, no need to create new free list node: - const skipfield_type prev_node_value = *( current_saved.skipfield_pointer - 1 ); - *( current_saved.skipfield_pointer - prev_node_value ) = prev_node_value + distance_to_iterator2; - *( iterator2.skipfield_pointer - 1 ) = prev_node_value + distance_to_iterator2; - } - - if( iterator1.element_pointer == begin_iterator.element_pointer ) { - begin_iterator = iterator2; - } - - iterator2.group_pointer->number_of_elements -= static_cast - ( number_of_group_erasures ); - total_number_of_elements -= number_of_group_erasures; - } else { // ie. full group erasure - if COLONY_CONSTEXPR( !( std::is_trivially_destructible::value ) ) { - while( current.element_pointer != iterator2.element_pointer ) { - COLONY_DESTROY( element_allocator_type, ( *this ), - reinterpret_cast( current.element_pointer ) ); - ++current.skipfield_pointer; - current.element_pointer += 1 + *current.skipfield_pointer; - current.skipfield_pointer += *current.skipfield_pointer; - } - } - - if( ( total_number_of_elements -= current.group_pointer->number_of_elements ) != 0 ) { - // ie. previous_group != NULL - current.group_pointer->previous_group->next_group = current.group_pointer->next_group; - end_iterator.group_pointer = current.group_pointer->previous_group; - end_iterator.element_pointer = current.group_pointer->previous_group->last_endpoint; - end_iterator.skipfield_pointer = current.group_pointer->previous_group->skipfield + - current.group_pointer->previous_group->capacity; - total_capacity -= current.group_pointer->capacity; - - if( current.group_pointer->free_list_head != std::numeric_limits::max() ) { - remove_from_groups_with_erasures_list( current.group_pointer ); - } - } else { // ie. colony is now empty - blank(); - } - - COLONY_DESTROY( group_allocator_type, group_allocator_pair, current.group_pointer ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, current.group_pointer, 1 ); - } - } - - /** Returns a boolean indicating whether the colony is currently empty of elements. */ - inline COLONY_FORCE_INLINE bool empty() const noexcept { - return total_number_of_elements == 0; - } - - /** Returns total number of elements currently stored in container. */ - inline size_type size() const noexcept { - return total_number_of_elements; - } - - /** Returns the maximum number of elements that the allocator can store in the container. */ - inline size_type max_size() const noexcept { - return std::allocator_traits::max_size( *this ); - } - - /** Returns total number of elements currently able to be stored in container without expansion. */ - inline size_type capacity() const noexcept { - return total_capacity; - } - - /** - * Returns the approximate memory use of the container plus it's elements. Will be - * inaccurate if the elements themselves dynamically-allocate data. Utility function - * primarily for benchmarking. - */ - inline size_type approximate_memory_use() const noexcept { - return - sizeof( *this ) + // sizeof colony basic structure - ( total_capacity * ( sizeof( aligned_element_type ) + sizeof( skipfield_type ) ) ) - + // sizeof current colony data capacity + skipfields - ( ( end_iterator.group_pointer == NULL ) ? 0 : ( ( end_iterator.group_pointer->group_number + 1 ) - * ( sizeof( group ) + sizeof( - skipfield_type ) ) ) ); // if colony not empty, add the memory usage of the group structures themselves, adding the extra skipfield node - } - - /** - * Changes the minimum and maximum internal group sizes, in terms of number of elements - * stored per group. If the colony is not empty and either min_group_size is larger than the - * smallest group in the colony, or max_group_size is smaller than the largest group in the - * colony, the colony will be internally copy-constructed into a new colony which uses the - * new group sizes, invalidating all pointers/iterators/references. If trying to change - * group sizes with a colony storing a non-copyable/movable type, please use the - * reinitialize function instead. - */ - void change_group_sizes( const skipfield_type min_allocation_amount, - const skipfield_type max_allocation_amount ) { - assert( ( min_allocation_amount > 2 ) & ( min_allocation_amount <= max_allocation_amount ) ); - - pointer_allocator_pair.min_elements_per_group = min_allocation_amount; - group_allocator_pair.max_elements_per_group = max_allocation_amount; - - if( begin_iterator.group_pointer != nullptr && - ( begin_iterator.group_pointer->capacity < min_allocation_amount || - end_iterator.group_pointer->capacity > max_allocation_amount ) ) { - consolidate(); - } - } - - /** - * Changes the minimum internal group size only, in terms of minimum number of elements - * stored per group. If the colony is not empty and min_group_size is larger than the - * smallest group in the colony, the colony will be internally move-constructed (if possible) - * or copy-constructed into a new colony which uses the new minimum group size, invalidating - * all pointers/iterators/references. If trying to change group sizes with a colony storing - * a non-copyable/movable type, please use the reinitialize function instead. - */ - inline void change_minimum_group_size( const skipfield_type min_allocation_amount ) { - change_group_sizes( min_allocation_amount, group_allocator_pair.max_elements_per_group ); - } - - /** - * Changes the maximum internal group size only, in terms of maximum number of elements - * stored per group. If the colony is not empty and either max_group_size is smaller than - * the largest group in the colony, the colony will be internally move-constructed (if - * possible) or copy-constructed into a new colony which uses the new maximum group size, - * invalidating all pointers/iterators/references. If trying to change group sizes with a - * colony storing a non-copyable/movable type, please use the reinitialize function instead. - */ - inline void change_maximum_group_size( const skipfield_type max_allocation_amount ) { - change_group_sizes( pointer_allocator_pair.min_elements_per_group, max_allocation_amount ); - } - - /** - * Sets @param minimum_group_size and @param maximum_group_size to the minimum and maximum - * group size respectively - */ - inline void get_group_sizes( skipfield_type &minimum_group_size, - skipfield_type &maximum_group_size ) const noexcept { - minimum_group_size = pointer_allocator_pair.min_elements_per_group; - maximum_group_size = group_allocator_pair.max_elements_per_group; - } - - /** - * Semantics of this function are the same as - * clear(); - * change_group_sizes(min_group_size, max_group_size); - * but without the move/copy-construction code of the change_group_sizes() function - this - * means it can be used with element types which are non-copy-constructible and - * non-move-constructible, unlike change_group_sizes(). - */ - inline void reinitialize( const skipfield_type min_allocation_amount, - const skipfield_type max_allocation_amount ) noexcept { - assert( ( min_allocation_amount > 2 ) & ( min_allocation_amount <= max_allocation_amount ) ); - clear(); - pointer_allocator_pair.min_elements_per_group = min_allocation_amount; - group_allocator_pair.max_elements_per_group = max_allocation_amount; - } - - /** - * Empties the colony and removes all elements and groups. - */ - inline COLONY_FORCE_INLINE void clear() noexcept { - destroy_all_data(); - blank(); - } - - /** - * Copy assignment: - * Copy the elements from another colony to this colony, clearing this colony of existing - * elements first. - */ - inline colony &operator=( const colony &source ) { - assert( &source != this ); - - destroy_all_data(); - colony temp( source ); - // Avoid generating 2nd temporary - *this = std::move( temp ); - - return *this; - } - - /** - * Move assignment: - * Move the elements from another colony to this colony, clearing this colony of existing - * elements first. Source colony is now empty and in a valid state (same as a new colony - * without any insertions), can be safely destructed or used in any regular way without - * problems. - */ - colony &operator=( colony &&source ) COLONY_NOEXCEPT_MOVE_ASSIGNMENT( allocator_type ) { - assert( &source != this ); - destroy_all_data(); - - if COLONY_CONSTEXPR( std::is_trivial::value && - std::is_trivial::value && std::is_trivial::value ) { - // NOLINTNEXTLINE(bugprone-undefined-memory-manipulation) - std::memcpy( static_cast( this ), &source, sizeof( colony ) ); - } else { - end_iterator = std::move( source.end_iterator ); - begin_iterator = std::move( source.begin_iterator ); - groups_with_erasures_list_head = source.groups_with_erasures_list_head; - total_number_of_elements = source.total_number_of_elements; - total_capacity = source.total_capacity; - pointer_allocator_pair.min_elements_per_group = - source.pointer_allocator_pair.min_elements_per_group; - group_allocator_pair.max_elements_per_group = source.group_allocator_pair.max_elements_per_group; - } - - source.blank(); - return *this; - } - - /** Compare contents of another colony to this colony. Returns true if they are equal. */ - bool operator==( const colony &rh ) const noexcept { - assert( this != &rh ); - - if( total_number_of_elements != rh.total_number_of_elements ) { - return false; - } - - for( iterator lh_iterator = begin_iterator, rh_iterator = rh.begin_iterator; - lh_iterator != end_iterator; ) { - if( *rh_iterator++ != *lh_iterator++ ) { - return false; - } - } - - return true; - } - - /** Compare contents of another colony to this colony. Returns true if they are unequal. */ - inline bool operator!=( const colony &rh ) const noexcept { - return !( *this == rh ); - } - - /** - * Reduces container capacity to the amount necessary to store all currently stored - * elements, consolidates elements and removes any erased locations. If the total number of - * elements is larger than the maximum group size, the resultant capacity will be equal to - * ((total_elements / max_group_size) + 1) * max_group_size (rounding down at division). - * Invalidates all pointers, iterators and references to elements within the container. - */ - void shrink_to_fit() { - if( total_number_of_elements == total_capacity ) { - return; - } else if( total_number_of_elements == 0 ) { // Edge case - clear(); - return; - } - - consolidate(); - } - - /** - * Preallocates memory space sufficient to store the number of elements indicated by - * reserve_amount. The maximum size for this number is currently limited to the maximum - * group size of the colony and will be rounded down if necessary. The default maximum group - * size is 65535 on the majority of platforms. The default minimum reserve amount is the - * same as the current minimum group size, and will be rounded up silently if necessary. - * This function is useful from a performance perspective when the user is inserting - * elements singly, but the overall number of insertions is known in advance. By reserving, - * colony can forgo creating many smaller memory block allocations (due to colony's growth - * factor) and reserve a single memory block instead. Alternatively one could simply change - * the default group sizes. - */ - void reserve( const size_type original_reserve_amount ) { - if( original_reserve_amount == 0 || original_reserve_amount <= total_capacity ) { - // Already have enough space allocated - return; - } - - skipfield_type reserve_amount; - - if( original_reserve_amount > static_cast - ( group_allocator_pair.max_elements_per_group ) ) { - reserve_amount = group_allocator_pair.max_elements_per_group; - } else if( original_reserve_amount < static_cast - ( pointer_allocator_pair.min_elements_per_group ) ) { - reserve_amount = pointer_allocator_pair.min_elements_per_group; - } else if( original_reserve_amount > max_size() ) { - reserve_amount = static_cast( max_size() ); - } else { - reserve_amount = static_cast( original_reserve_amount ); - } - - if( total_number_of_elements == 0 ) { // Most common scenario - empty colony - if( begin_iterator.group_pointer != nullptr ) { - // Edge case - empty colony but first group is initialized ie. had some insertions but all elements got subsequently erased - COLONY_DESTROY( group_allocator_type, group_allocator_pair, begin_iterator.group_pointer ); - COLONY_DEALLOCATE( group_allocator_type, group_allocator_pair, begin_iterator.group_pointer, 1 ); - } // else: Empty colony, no insertions yet, time to allocate - - initialize( reserve_amount ); - // last_endpoint initially == elements + 1 via default constructor - begin_iterator.group_pointer->last_endpoint = begin_iterator.group_pointer->elements; - begin_iterator.group_pointer->number_of_elements = 0; // 1 by default - } else { // Non-empty colony, don't have enough space allocated - const skipfield_type original_min_elements = pointer_allocator_pair.min_elements_per_group; - // Make sure all groups are at maximum appropriate capacity (this amount already rounded down to a skipfield type earlier in function) - pointer_allocator_pair.min_elements_per_group = static_cast( reserve_amount ); - consolidate(); - pointer_allocator_pair.min_elements_per_group = original_min_elements; - } - } - - /** - * Increments/decrements the iterator supplied by the positive or negative amount indicated - * by distance. Speed of incrementation will almost always be faster than using the ++ - * operator on the iterator for increments greater than 1. In some cases it may approximate - * O(1). The iterator_type can be an iterator, const_iterator, reverse_iterator or - * const_reverse_iterator. - */ - // Advance implementation for iterator and const_iterator: - // Cannot be noexcept due to the possibility of an uninitialized iterator - template - void advance( colony_iterator &it, difference_type distance ) const { - // For code simplicity - should hopefully be optimized out by compiler: - group_pointer_type &group_pointer = it.group_pointer; - aligned_pointer_type &element_pointer = it.element_pointer; - skipfield_pointer_type &skipfield_pointer = it.skipfield_pointer; - - // covers uninitialized colony_iterator && empty group - assert( group_pointer != nullptr ); - - // Now, run code based on the nature of the distance type - negative, positive or zero: - if( distance > 0 ) { // ie. += - // Code explanation: - // For the initial state of the iterator, we don't know how what elements have been erased before that element in that group. - // So for the first group, we follow the following logic: - // 1. If no elements have been erased in the group, we do simple addition to progress either to within the group (if the distance is small enough) or the end of the group and subtract from distance accordingly. - // 2. If any of the first group elements have been erased, we manually iterate, as we don't know whether the erased elements occur before or after the initial iterator position, and we subtract 1 from the distance amount each time. Iteration continues until either distance becomes zero, or we reach the end of the group. - - // For all subsequent groups, we follow this logic: - // 1. If distance is larger than the total number of non-erased elements in a group, we skip that group and subtract the number of elements in that group from distance - // 2. If distance is smaller than the total number of non-erased elements in a group, then: - // a. if there're no erased elements in the group we simply add distance to group->elements to find the new location for the iterator - // b. if there are erased elements in the group, we manually iterate and subtract 1 from distance on each iteration, until the new iterator location is found ie. distance = 0 - - // Note: incrementing element_pointer is avoided until necessary to avoid needless calculations - - // Check that we're not already at end() - assert( !( element_pointer == group_pointer->last_endpoint && - group_pointer->next_group == nullptr ) ); - - // Special case for initial element pointer and initial group (we don't know how far into the group the element pointer is) - if( element_pointer != group_pointer->elements + * ( group_pointer->skipfield ) ) { - // ie. != first non-erased element in group - const difference_type distance_from_end = static_cast - ( group_pointer->last_endpoint - element_pointer ); - - if( group_pointer->number_of_elements == static_cast( distance_from_end ) ) { - // ie. if there are no erasures in the group (using endpoint - elements_start to determine number of elements in group just in case this is the last group of the colony, in which case group->last_endpoint != group->elements + group->capacity) - if( distance < distance_from_end ) { - element_pointer += distance; - skipfield_pointer += distance; - return; - } else if( group_pointer->next_group == nullptr ) { - // either we've reached end() or gone beyond it, so bound to end() - element_pointer = group_pointer->last_endpoint; - skipfield_pointer += distance_from_end; - return; - } else { - distance -= distance_from_end; - } - } else { - const skipfield_pointer_type endpoint = skipfield_pointer + distance_from_end; - - while( true ) { - ++skipfield_pointer; - skipfield_pointer += *skipfield_pointer; - --distance; - - if( skipfield_pointer == endpoint ) { - break; - } else if( distance == 0 ) { - element_pointer = group_pointer->elements + ( skipfield_pointer - group_pointer->skipfield ); - return; - } - } - - if( group_pointer->next_group == nullptr ) { - // either we've reached end() or gone beyond it, so bound to end() - element_pointer = group_pointer->last_endpoint; - return; - } - } - - group_pointer = group_pointer->next_group; - - if( distance == 0 ) { - element_pointer = group_pointer->elements + *( group_pointer->skipfield ); - skipfield_pointer = group_pointer->skipfield + *( group_pointer->skipfield ); - return; - } - } - - // Intermediary groups - at the start of this code block and the subsequent block, the position of the iterator is assumed to be the first non-erased element in the current group: - while( static_cast( group_pointer->number_of_elements ) <= distance ) { - if( group_pointer->next_group == nullptr ) { - // either we've reached end() or gone beyond it, so bound to end() - element_pointer = group_pointer->last_endpoint; - skipfield_pointer = group_pointer->skipfield + ( group_pointer->last_endpoint - - group_pointer->elements ); - return; - } else if( ( distance -= group_pointer->number_of_elements ) == 0 ) { - group_pointer = group_pointer->next_group; - element_pointer = group_pointer->elements + *( group_pointer->skipfield ); - skipfield_pointer = group_pointer->skipfield + *( group_pointer->skipfield ); - return; - } else { - group_pointer = group_pointer->next_group; - } - } - - // Final group (if not already reached): - if( group_pointer->free_list_head == std::numeric_limits::max() ) { - // No erasures in this group, use straight pointer addition - element_pointer = group_pointer->elements + distance; - skipfield_pointer = group_pointer->skipfield + distance; - return; - } else { // ie. number_of_elements > distance - safe to ignore endpoint check condition while incrementing: - skipfield_pointer = group_pointer->skipfield + *( group_pointer->skipfield ); - - do { - ++skipfield_pointer; - skipfield_pointer += *skipfield_pointer; - } while( --distance != 0 ); - - element_pointer = group_pointer->elements + ( skipfield_pointer - group_pointer->skipfield ); - return; - } - - return; - } else if( distance < 0 ) { // for negative change - // Code logic is very similar to += above - - // check that we're not already at begin() - assert( !( ( element_pointer == group_pointer->elements + * ( group_pointer->skipfield ) ) && - group_pointer->previous_group == nullptr ) ); - distance = -distance; - - // Special case for initial element pointer and initial group (we don't know how far into the group the element pointer is) - if( element_pointer != group_pointer->last_endpoint ) { // ie. != end() - if( group_pointer->free_list_head == std::numeric_limits::max() ) { - // ie. no prior erasures have occurred in this group - const difference_type distance_from_beginning = static_cast - ( element_pointer - group_pointer->elements ); - - if( distance <= distance_from_beginning ) { - element_pointer -= distance; - skipfield_pointer -= distance; - return; - } else if( group_pointer->previous_group == nullptr ) { - // ie. we've gone before begin(), so bound to begin() - element_pointer = group_pointer->elements; - skipfield_pointer = group_pointer->skipfield; - return; - } else { - distance -= distance_from_beginning; - } - } else { - const skipfield_pointer_type beginning_point = group_pointer->skipfield + * - ( group_pointer->skipfield ); - - while( skipfield_pointer != beginning_point ) { - --skipfield_pointer; - skipfield_pointer -= *skipfield_pointer; - - if( --distance == 0 ) { - element_pointer = group_pointer->elements + ( skipfield_pointer - group_pointer->skipfield ); - return; - } - } - - if( group_pointer->previous_group == nullptr ) { - // This is first group, so bound to begin() (just in case final decrement took us before begin()) - element_pointer = group_pointer->elements + *( group_pointer->skipfield ); - skipfield_pointer = group_pointer->skipfield + *( group_pointer->skipfield ); - return; - } - } - - group_pointer = group_pointer->previous_group; - } - - // Intermediary groups - at the start of this code block and the subsequent block, the position of the iterator is assumed to be either the first non-erased element in the next group over, or end(): - while( static_cast( group_pointer->number_of_elements ) < distance ) { - if( group_pointer->previous_group == nullptr ) { - // we've gone beyond begin(), so bound to it - element_pointer = group_pointer->elements + *( group_pointer->skipfield ); - skipfield_pointer = group_pointer->skipfield + *( group_pointer->skipfield ); - return; - } - - distance -= group_pointer->number_of_elements; - group_pointer = group_pointer->previous_group; - } - - // Final group (if not already reached): - if( static_cast( group_pointer->number_of_elements ) == distance ) { - element_pointer = group_pointer->elements + *( group_pointer->skipfield ); - skipfield_pointer = group_pointer->skipfield + *( group_pointer->skipfield ); - return; - } else if( group_pointer->free_list_head == std::numeric_limits::max() ) { - // ie. no erased elements in this group - element_pointer = reinterpret_cast( group_pointer->skipfield ) - distance; - skipfield_pointer = ( group_pointer->skipfield + group_pointer->capacity ) - distance; - return; - } else { // ie. no more groups to traverse but there are erased elements in this group - skipfield_pointer = group_pointer->skipfield + group_pointer->capacity; - - do { - --skipfield_pointer; - skipfield_pointer -= *skipfield_pointer; - } while( --distance != 0 ); - - element_pointer = group_pointer->elements + ( skipfield_pointer - group_pointer->skipfield ); - return; - } - } - // Only distance == 0 reaches here - } - - // Advance for reverse_iterator and const_reverse_iterator - this needs to be implemented slightly differently to forward-iterator's advance, as it needs to be able to reach rend() (ie. begin() - 1) and to be bounded by rbegin(): - template - void advance( colony_reverse_iterator &reverse_it, - difference_type distance ) const { // could cause exception if iterator is uninitialized - group_pointer_type &group_pointer = reverse_it.it.group_pointer; - aligned_pointer_type &element_pointer = reverse_it.it.element_pointer; - skipfield_pointer_type &skipfield_pointer = reverse_it.it.skipfield_pointer; - - assert( element_pointer != nullptr ); - - if( distance > 0 ) { - // Check that we're not already at rend() - assert( !( element_pointer == group_pointer->elements - 1 && - group_pointer->previous_group == nullptr ) ); - // Special case for initial element pointer and initial group (we don't know how far into the group the element pointer is) - // Since a reverse_iterator cannot == last_endpoint (ie. before rbegin()) we don't need to check for that like with iterator - if( group_pointer->free_list_head == std::numeric_limits::max() ) { - difference_type distance_from_beginning = static_cast - ( element_pointer - group_pointer->elements ); - - if( distance <= distance_from_beginning ) { - element_pointer -= distance; - skipfield_pointer -= distance; - return; - } else if( group_pointer->previous_group == nullptr ) { - // Either we've reached rend() or gone beyond it, so bound to rend() - element_pointer = group_pointer->elements - 1; - skipfield_pointer = group_pointer->skipfield - 1; - return; - } else { - distance -= distance_from_beginning; - } - } else { - const skipfield_pointer_type beginning_point = group_pointer->skipfield + * - ( group_pointer->skipfield ); - - while( skipfield_pointer != beginning_point ) { - --skipfield_pointer; - skipfield_pointer -= *skipfield_pointer; - - if( --distance == 0 ) { - element_pointer = group_pointer->elements + ( skipfield_pointer - group_pointer->skipfield ); - return; - } - } - - if( group_pointer->previous_group == nullptr ) { - // If we've reached rend(), bound to that - element_pointer = group_pointer->elements - 1; - skipfield_pointer = group_pointer->skipfield - 1; - return; - } - } - - group_pointer = group_pointer->previous_group; - - // Intermediary groups - at the start of this code block and the subsequent block, the position of the iterator is assumed to be the first non-erased element in the next group: - while( static_cast( group_pointer->number_of_elements ) < distance ) { - if( group_pointer->previous_group == nullptr ) { // bound to rend() - element_pointer = group_pointer->elements - 1; - skipfield_pointer = group_pointer->skipfield - 1; - return; - } - - distance -= static_cast( group_pointer->number_of_elements ); - group_pointer = group_pointer->previous_group; - } - - // Final group (if not already reached) - if( static_cast( group_pointer->number_of_elements ) == distance ) { - element_pointer = group_pointer->elements + *( group_pointer->skipfield ); - skipfield_pointer = group_pointer->skipfield + *( group_pointer->skipfield ); - return; - } else if( group_pointer->free_list_head == std::numeric_limits::max() ) { - element_pointer = reinterpret_cast( group_pointer->skipfield ) - distance; - skipfield_pointer = ( group_pointer->skipfield + group_pointer->capacity ) - distance; - return; - } else { - skipfield_pointer = group_pointer->skipfield + group_pointer->capacity; - - do { - --skipfield_pointer; - skipfield_pointer -= *skipfield_pointer; - } while( --distance != 0 ); - - element_pointer = group_pointer->elements + ( skipfield_pointer - group_pointer->skipfield ); - return; - } - } else if( distance < 0 ) { - // Check that we're not already at rbegin() - assert( !( ( element_pointer == ( group_pointer->last_endpoint - 1 ) - * - ( group_pointer->skipfield + ( group_pointer->last_endpoint - group_pointer->elements ) - 1 ) ) && - group_pointer->next_group == nullptr ) ); - - if( element_pointer != group_pointer->elements + * ( group_pointer->skipfield ) ) { - // ie. != first non-erased element in group - if( group_pointer->free_list_head == std::numeric_limits::max() ) { - // ie. if there are no erasures in the group - const difference_type distance_from_end = static_cast - ( group_pointer->last_endpoint - element_pointer ); - - if( distance < distance_from_end ) { - element_pointer += distance; - skipfield_pointer += distance; - return; - } else if( group_pointer->next_group == nullptr ) { // bound to rbegin() - // no erasures so we don't have to subtract skipfield value as we do below - element_pointer = group_pointer->last_endpoint - 1; - skipfield_pointer += distance_from_end - 1; - return; - } else { - distance -= distance_from_end; - } - } else { - const skipfield_pointer_type endpoint = skipfield_pointer + ( group_pointer->last_endpoint - - element_pointer ); - - while( true ) { - ++skipfield_pointer; - skipfield_pointer += *skipfield_pointer; - --distance; - - if( skipfield_pointer == endpoint ) { - break; - } else if( distance == 0 ) { - element_pointer = group_pointer->elements + ( skipfield_pointer - group_pointer->skipfield ); - return; - } - } - - if( group_pointer->next_group == nullptr ) { // bound to rbegin() - --skipfield_pointer; - element_pointer = ( group_pointer->last_endpoint - 1 ) - *skipfield_pointer; - skipfield_pointer -= *skipfield_pointer; - return; - } - } - - group_pointer = group_pointer->next_group; - - if( distance == 0 ) { - element_pointer = group_pointer->elements + *( group_pointer->skipfield ); - skipfield_pointer = group_pointer->skipfield + *( group_pointer->skipfield ); - return; - } - } - - // Intermediary groups - at the start of this code block and the subsequent block, the position of the iterator is assumed to be the first non-erased element in the current group, as a result of the previous code blocks: - while( static_cast( group_pointer->number_of_elements ) <= distance ) { - if( group_pointer->next_group == nullptr ) { // bound to rbegin() - skipfield_pointer = group_pointer->skipfield + ( group_pointer->last_endpoint - - group_pointer->elements ) - 1; - --skipfield_pointer; - element_pointer = ( group_pointer->last_endpoint - 1 ) - *skipfield_pointer; - skipfield_pointer -= *skipfield_pointer; - return; - } else if( ( distance -= group_pointer->number_of_elements ) == 0 ) { - group_pointer = group_pointer->next_group; - element_pointer = group_pointer->elements + *( group_pointer->skipfield ); - skipfield_pointer = group_pointer->skipfield + *( group_pointer->skipfield ); - return; - } else { - group_pointer = group_pointer->next_group; - } - } - - // Final group (if not already reached): - if( group_pointer->free_list_head == std::numeric_limits::max() ) { - // No erasures in this group, use straight pointer addition - element_pointer = group_pointer->elements + distance; - skipfield_pointer = group_pointer->skipfield + distance; - return; - } else { // ie. number_of_elements > distance - safe to ignore endpoint check condition while incrementing: - skipfield_pointer = group_pointer->skipfield + *( group_pointer->skipfield ); - - do { - ++skipfield_pointer; - skipfield_pointer += *skipfield_pointer; - } while( --distance != 0 ); - - element_pointer = group_pointer->elements + ( skipfield_pointer - group_pointer->skipfield ); - return; - } - - return; - } - } - - /** - * Creates a copy of the iterator supplied, then increments/decrements this iterator by - * the positive or negative amount indicated by distance. - */ - template - inline colony_iterator next( const colony_iterator &it, - const typename colony_iterator::difference_type distance = 1 ) const { - colony_iterator return_iterator( it ); - advance( return_iterator, distance ); - return return_iterator; - } - - /** - * Creates a copy of the iterator supplied, then increments/decrements this iterator by - * the positive or negative amount indicated by distance. - */ - template - inline colony_reverse_iterator next( const colony_reverse_iterator &it, - const typename colony_reverse_iterator::difference_type distance = 1 ) const { - colony_reverse_iterator return_iterator( it ); - advance( return_iterator, distance ); - return return_iterator; - } - - /** - * Creates a copy of the iterator supplied, then decrements/increments this iterator by the - * positive or negative amount indicated by distance. - */ - template - inline colony_iterator prev( const colony_iterator &it, - const typename colony_iterator::difference_type distance = 1 ) const { - colony_iterator return_iterator( it ); - advance( return_iterator, -distance ); - return return_iterator; - } - - /** - * Creates a copy of the iterator supplied, then decrements/increments this iterator by the - * positive or negative amount indicated by distance. - */ - template - inline colony_reverse_iterator prev( const colony_reverse_iterator &it, - const typename colony_reverse_iterator::difference_type distance = 1 ) const { - colony_reverse_iterator return_iterator( it ); - advance( return_iterator, -distance ); - return return_iterator; - } - - /** - * Measures the distance between two iterators, returning the result, which will be negative - * if the second iterator supplied is before the first iterator supplied in terms of its - * location in the colony. - */ - template - typename colony_iterator::difference_type distance( const colony_iterator - &first, const colony_iterator &last ) const { - // Code logic: - // If iterators are the same, return 0 - // Otherwise, find which iterator is later in colony, copy that to iterator2. Copy the lower to iterator1. - // If they are not pointing to elements in the same group, process the intermediate groups and add distances, - // skipping manual incrementation in all but the initial and final groups. - // In the initial and final groups, manual incrementation must be used to calculate distance, if there have been no prior erasures in those groups. - // If there are no prior erasures in either of those groups, we can use pointer arithmetic to calculate the distances for those groups. - - assert( !( first.group_pointer == nullptr ) && - !( last.group_pointer == nullptr ) ); // Check that they are initialized - - if( last.element_pointer == first.element_pointer ) { - return 0; - } - - using iterator_type = colony_iterator; - using diff_type = typename iterator_type::difference_type; - diff_type distance = 0; - - iterator_type iterator1 = first, iterator2 = last; - const bool swap = first > last; - - if( swap ) { // Less common case - iterator1 = last; - iterator2 = first; - } - - if( iterator1.group_pointer != iterator2.group_pointer ) { - // if not in same group, process intermediate groups - - // Process initial group: - if( iterator1.group_pointer->free_list_head == std::numeric_limits::max() ) { - // If no prior erasures have occured in this group we can do simple addition - distance += static_cast( iterator1.group_pointer->last_endpoint - - iterator1.element_pointer ); - } else if( iterator1.element_pointer == iterator1.group_pointer->elements ) { - // ie. element is at start of group - rare case - distance += static_cast( iterator1.group_pointer->number_of_elements ); - } else { - const skipfield_pointer_type endpoint = iterator1.skipfield_pointer + - ( iterator1.group_pointer->last_endpoint - iterator1.element_pointer ); - - while( iterator1.skipfield_pointer != endpoint ) { - iterator1.skipfield_pointer += *( ++iterator1.skipfield_pointer ); - ++distance; - } - } - - // Process all other intermediate groups: - iterator1.group_pointer = iterator1.group_pointer->next_group; - - while( iterator1.group_pointer != iterator2.group_pointer ) { - distance += static_cast( iterator1.group_pointer->number_of_elements ); - iterator1.group_pointer = iterator1.group_pointer->next_group; - } - - iterator1.skipfield_pointer = iterator1.group_pointer->skipfield; - } - - if( iterator1.group_pointer->free_list_head == std::numeric_limits::max() ) { - // ie. no erasures in this group, direct subtraction is possible - distance += static_cast( iterator2.skipfield_pointer - iterator1.skipfield_pointer ); - } else if( iterator1.group_pointer->last_endpoint - 1 >= iterator2.element_pointer ) { - // ie. if iterator2 is .end() or 1 before - distance += static_cast( iterator1.group_pointer->number_of_elements - - ( iterator1.group_pointer->last_endpoint - iterator2.element_pointer ) ); - } else { - while( iterator1.skipfield_pointer != iterator2.skipfield_pointer ) { - iterator1.skipfield_pointer += *( ++iterator1.skipfield_pointer ); - ++distance; - } - } - - if( swap ) { - distance = -distance; - } - - return distance; - } - - template - inline typename colony_reverse_iterator::difference_type distance( - const colony_reverse_iterator &first, - const colony_reverse_iterator &last ) const { - return distance( last.it, first.it ); - } - - // Type-changing functions: - - /** - * Getting a pointer from an iterator is simple - simply dereference it then grab the - * address ie. "&(*the_iterator);". Getting an iterator from a pointer is typically not so - * simple. This function enables the user to do exactly that. This is expected to be useful - * in the use-case where external containers are storing pointers to colony elements instead - * of iterators (as iterators for colonies have 3 times the size of an element pointer) and - * the program wants to erase the element being pointed to or possibly change the element - * being pointed to. Converting a pointer to an iterator using this method and then erasing, - * is about 20% slower on average than erasing when you already have the iterator. This is - * less dramatic than it sounds, as it is still faster than all std:: container erasure - * times which it is roughly equal to. However this is generally a slower, lookup-based - * operation. If the lookup doesn't find a non-erased element based on that pointer, it - * returns end(). Otherwise it returns an iterator pointing to the element in question. - * - * Cannot be noexcept as colony could be empty or pointer invalid - */ - iterator get_iterator_from_pointer( const pointer element_pointer ) const { - assert( !empty() ); - assert( element_pointer != nullptr ); - - // Start with last group first, as will be the largest group - group_pointer_type current_group = end_iterator.group_pointer; - - while( current_group != nullptr ) { - if( reinterpret_cast( element_pointer ) >= current_group->elements && - reinterpret_cast( element_pointer ) < reinterpret_cast - ( current_group->skipfield ) ) { - const skipfield_pointer_type skipfield_pointer = current_group->skipfield + - ( reinterpret_cast( element_pointer ) - current_group->elements ); - return ( *skipfield_pointer == 0 ) ? iterator( current_group, - reinterpret_cast( element_pointer ), - skipfield_pointer ) : end_iterator; // If element has been erased, return end() - } - - current_group = current_group->previous_group; - } - - return end_iterator; - } - - /** - * While colony is a container with unordered insertion (and is therefore unordered), it - * still has a (transitory) order which changes upon any erasure or insertion. Temporary - * index numbers are therefore obtainable. These can be useful, for example, when creating - * a save file in a computer game, where certain elements in a container may need to be - * re-linked to other elements in other container upon reloading the save file. - * - * may throw exception if iterator is invalid/uninitialized - */ - template - size_type get_index_from_iterator( const colony_iterator &it ) const { - assert( !empty() ); - assert( it.group_pointer != nullptr ); - - // This is essentially a simplified version of distance() optimized for counting from begin() - size_type index = 0; - group_pointer_type group_pointer = begin_iterator.group_pointer; - - // For all prior groups, add group sizes - while( group_pointer != it.group_pointer ) { - index += static_cast( group_pointer->number_of_elements ); - group_pointer = group_pointer->next_group; - } - - if( group_pointer->free_list_head == std::numeric_limits::max() ) { - // If no erased elements in group exist, do straight pointer arithmetic to get distance to start for first element - index += static_cast( it.element_pointer - group_pointer->elements ); - } else { - // Otherwise do manual ++ loop - count from beginning of group until location is reached - skipfield_pointer_type skipfield_pointer = group_pointer->skipfield + *( group_pointer->skipfield ); - - while( skipfield_pointer != it.skipfield_pointer ) { - ++skipfield_pointer; - skipfield_pointer += *skipfield_pointer; - ++index; - } - } - - return index; - } - - /** - * The same as get_index_from_iterator, but for reverse_iterators and const_reverse_iterators. - * Index is from front of colony (same as iterator), not back of colony. - */ - template - inline size_type get_index_from_reverse_iterator( const colony_reverse_iterator - &rev_iterator ) const { - return get_index_from_iterator( rev_iterator.it ); - } - - /** - * As described above, there may be situations where obtaining iterators to specific - * elements based on an index can be useful, for example, when reloading save files. This - * function is basically a shorthand to avoid typing - * iterator it = colony.begin(); - * colony.advance(it, index); - * - * Cannot be noexcept as colony could be empty - */ - template - iterator get_iterator_from_index( const index_type index ) const { - assert( !empty() ); - assert( std::numeric_limits::is_integer ); - - iterator it( begin_iterator ); - advance( it, static_cast( index ) ); - return it; - } - - /** Returns a copy of the allocator used by the colony instance. */ - inline allocator_type get_allocator() const noexcept { - return element_allocator_type(); - } - - private: - - struct less { - bool operator()( const element_type &a, const element_type &b ) const noexcept { - return a < b; - } - }; - - // Function-object, used to redirect the sort function to compare element pointers by the elements they point to, and sort the element pointers instead of the elements: - template - struct sort_dereferencer { - comparison_function stored_instance; - - explicit sort_dereferencer( const comparison_function &function_instance ): - stored_instance( function_instance ) - {} - - sort_dereferencer() noexcept = default; - - bool operator()( const pointer first, const pointer second ) { - return stored_instance( *first, *second ); - } - }; - - public: - - /** - * Sort the content of the colony. By default this compares the colony content using a - * less-than operator, unless the user supplies a comparison function (ie. same conditions - * as std::list's sort function). - * - * Uses std::sort internally. - */ - template - void sort( comparison_function compare ) { - if( total_number_of_elements < 2 ) { - return; - } - - pointer *const element_pointers = COLONY_ALLOCATE( pointer_allocator_type, pointer_allocator_pair, - total_number_of_elements, nullptr ); - pointer *element_pointer = element_pointers; - - // Construct pointers to all elements in the colony in sequence: - for( iterator current_element = begin_iterator; current_element != end_iterator; - ++current_element ) { - COLONY_CONSTRUCT( pointer_allocator_type, pointer_allocator_pair, element_pointer++, - &*current_element ); - } - - // Now, sort the pointers by the values they point to: - std::sort( element_pointers, element_pointers + total_number_of_elements, - sort_dereferencer( compare ) ); - - // Create a new colony and copy the elements from the old one to the new one in the order of the sorted pointers: - colony new_location; - new_location.change_group_sizes( pointer_allocator_pair.min_elements_per_group, - group_allocator_pair.max_elements_per_group ); - - try { - new_location.reserve( static_cast( ( total_number_of_elements > - std::numeric_limits::max() ) ? std::numeric_limits::max() : - total_number_of_elements ) ); - - if COLONY_CONSTEXPR( std::is_move_constructible::value ) { - for( pointer *current_element_pointer = element_pointers; - current_element_pointer != element_pointer; ++current_element_pointer ) { - new_location.insert( std::move( *reinterpret_cast( *current_element_pointer ) ) ); - } - } else { - for( pointer *current_element_pointer = element_pointers; - current_element_pointer != element_pointer; ++current_element_pointer ) { - new_location.insert( *reinterpret_cast( *current_element_pointer ) ); - } - } - } catch( ... ) { - if COLONY_CONSTEXPR( !std::is_trivially_destructible::value ) { - for( pointer *current_element_pointer = element_pointers; - current_element_pointer != element_pointer; ++current_element_pointer ) { - COLONY_DESTROY( pointer_allocator_type, pointer_allocator_pair, current_element_pointer ); - } - } - - COLONY_DEALLOCATE( pointer_allocator_type, pointer_allocator_pair, element_pointers, - total_number_of_elements ); - throw; - } - - // Make the old colony the new one, destroy the old one's data/sequence: - *this = std::move( new_location ); // avoid generating temporary - - if COLONY_CONSTEXPR( !std::is_trivially_destructible::value ) { - for( pointer *current_element_pointer = element_pointers; - current_element_pointer != element_pointer; ++current_element_pointer ) { - COLONY_DESTROY( pointer_allocator_type, pointer_allocator_pair, current_element_pointer ); - } - } - - COLONY_DEALLOCATE( pointer_allocator_type, pointer_allocator_pair, element_pointers, - total_number_of_elements ); - } - - inline void sort() { - sort( less() ); - } - - /** - * Transfer all elements from source colony into destination colony without invalidating - * pointers/iterators to either colony's elements (in other words the destination takes - * ownership of the source's memory blocks). After the splice, the source colony is empty. - * Splicing is much faster than range-moving or copying all elements from one colony to - * another. Colony does not guarantee a particular order of elements after splicing, for - * performance reasons; the insertion location of source elements in the destination colony - * is chosen based on the most positive performance outcome for subsequent iterations/ - * insertions. For example if the destination colony is {1, 2, 3, 4} and the source colony - * is {5, 6, 7, 8} the destination colony post-splice could be {1, 2, 3, 4, 5, 6, 7, 8} or - * {5, 6, 7, 8, 1, 2, 3, 4}, depending on internal state of both colonies and prior - * insertions/erasures. - */ - void splice( colony &source ) COLONY_NOEXCEPT_SWAP( allocator_type ) { - // Process: if there are unused memory spaces at the end of the current back group of the chain, convert them - // to skipped elements and add the locations to the group's free list. - // Then link the destination's groups to the source's groups and nullify the source. - // If the source has more unused memory spaces in the back group than the destination, swap them before processing to reduce the number of locations added to a free list and also subsequent jumps during iteration. - - assert( &source != this ); - - if( source.total_number_of_elements == 0 ) { - return; - } else if( total_number_of_elements == 0 ) { - *this = std::move( source ); - return; - } - - // If there's more unused element locations at end of destination than source, swap with source to reduce number of skipped elements and size of free-list: - if( ( reinterpret_cast( end_iterator.group_pointer->skipfield ) - - end_iterator.element_pointer ) > ( reinterpret_cast - ( source.end_iterator.group_pointer->skipfield ) - source.end_iterator.element_pointer ) ) { - swap( source ); - } - - // Correct group sizes if necessary: - if( source.pointer_allocator_pair.min_elements_per_group < - pointer_allocator_pair.min_elements_per_group ) { - pointer_allocator_pair.min_elements_per_group = - source.pointer_allocator_pair.min_elements_per_group; - } - - if( source.group_allocator_pair.max_elements_per_group > - group_allocator_pair.max_elements_per_group ) { - group_allocator_pair.max_elements_per_group = source.group_allocator_pair.max_elements_per_group; - } - - // Add source list of groups-with-erasures to destination list of groups-with-erasures: - if( source.groups_with_erasures_list_head != nullptr ) { - if( groups_with_erasures_list_head != nullptr ) { - group_pointer_type tail_group = groups_with_erasures_list_head; - - while( tail_group->erasures_list_next_group != nullptr ) { - tail_group = tail_group->erasures_list_next_group; - } - - tail_group->erasures_list_next_group = source.groups_with_erasures_list_head; - } else { - groups_with_erasures_list_head = source.groups_with_erasures_list_head; - } - } - - const skipfield_type distance_to_end = static_cast - ( reinterpret_cast( end_iterator.group_pointer->skipfield ) - - end_iterator.element_pointer ); - - if( distance_to_end != 0 ) { // 0 == edge case - // Mark unused element memory locations from back group as skipped/erased: - - // Update skipfield: - const skipfield_type previous_node_value = *( end_iterator.skipfield_pointer - 1 ); - end_iterator.group_pointer->last_endpoint = reinterpret_cast - ( end_iterator.group_pointer->skipfield ); - - if( previous_node_value == 0 ) { // no previous skipblock - *end_iterator.skipfield_pointer = distance_to_end; - *( end_iterator.skipfield_pointer + distance_to_end - 1 ) = distance_to_end; - - const skipfield_type index = static_cast( end_iterator.element_pointer - - end_iterator.group_pointer->elements ); - - if( end_iterator.group_pointer->free_list_head != std::numeric_limits::max() ) { - // ie. if this group already has some erased elements - // set prev free list head's 'next index' number to the index of the current element - *( reinterpret_cast( end_iterator.group_pointer->elements + - end_iterator.group_pointer->free_list_head ) + 1 ) = index; - } else { - // add it to the groups-with-erasures free list - end_iterator.group_pointer->erasures_list_next_group = groups_with_erasures_list_head; - groups_with_erasures_list_head = end_iterator.group_pointer; - } - - *( reinterpret_cast( end_iterator.element_pointer ) ) = - end_iterator.group_pointer->free_list_head; - *( reinterpret_cast( end_iterator.element_pointer ) + 1 ) = - std::numeric_limits::max(); - end_iterator.group_pointer->free_list_head = index; - } else { - // update previous skipblock, no need to update free list: - *( end_iterator.skipfield_pointer - previous_node_value ) = *( end_iterator.skipfield_pointer + - distance_to_end - 1 ) = previous_node_value + distance_to_end; - } - } - - // Update subsequent group numbers: - group_pointer_type current_group = source.begin_iterator.group_pointer; - size_type current_group_number = end_iterator.group_pointer->group_number; - - do { - current_group->group_number = ++current_group_number; - current_group = current_group->next_group; - } while( current_group != nullptr ); - - // Join the destination and source group chains: - end_iterator.group_pointer->next_group = source.begin_iterator.group_pointer; - source.begin_iterator.group_pointer->previous_group = end_iterator.group_pointer; - end_iterator = source.end_iterator; - total_number_of_elements += source.total_number_of_elements; - source.blank(); - } - - /** Swaps the colony's contents with that of source. */ - void swap( colony &source ) COLONY_NOEXCEPT_SWAP( allocator_type ) { - assert( &source != this ); - - // if all pointer types are trivial we can just copy using memcpy - avoids constructors/destructors etc and is faster - if COLONY_CONSTEXPR( std::is_trivial::value && - std::is_trivial::value && - std::is_trivial::value ) { - char temp[sizeof( colony )]; - std::memcpy( &temp, static_cast( this ), sizeof( colony ) ); - std::memcpy( static_cast( this ), static_cast( &source ), sizeof( colony ) ); - std::memcpy( static_cast( &source ), &temp, sizeof( colony ) ); - - // If pointer types are not trivial, moving them is probably going to be more efficient than copying them below - } else if COLONY_CONSTEXPR( std::is_move_assignable::value && - std::is_move_assignable::value && - std::is_move_assignable::value && - std::is_move_constructible::value && - std::is_move_constructible::value && - std::is_move_constructible::value ) { - colony temp( std::move( source ) ); - source = std::move( *this ); - *this = std::move( temp ); - } else { - const iterator swap_end_iterator = end_iterator; - const iterator swap_begin_iterator = begin_iterator; - const group_pointer_type swap_groups_with_erasures_list_head = groups_with_erasures_list_head; - const size_type swap_total_number_of_elements = total_number_of_elements; - const size_type swap_total_capacity = total_capacity; - const skipfield_type swap_min_elements_per_group = pointer_allocator_pair.min_elements_per_group; - const skipfield_type swap_max_elements_per_group = group_allocator_pair.max_elements_per_group; - - end_iterator = source.end_iterator; - begin_iterator = source.begin_iterator; - groups_with_erasures_list_head = source.groups_with_erasures_list_head; - total_number_of_elements = source.total_number_of_elements; - total_capacity = source.total_capacity; - pointer_allocator_pair.min_elements_per_group = - source.pointer_allocator_pair.min_elements_per_group; - group_allocator_pair.max_elements_per_group = source.group_allocator_pair.max_elements_per_group; - - source.end_iterator = swap_end_iterator; - source.begin_iterator = swap_begin_iterator; - source.groups_with_erasures_list_head = swap_groups_with_erasures_list_head; - source.total_number_of_elements = swap_total_number_of_elements; - source.total_capacity = swap_total_capacity; - source.pointer_allocator_pair.min_elements_per_group = swap_min_elements_per_group; - source.group_allocator_pair.max_elements_per_group = swap_max_elements_per_group; - } - } - -}; // colony - -/** - * Swaps colony A's contents with that of colony B. - * Assumes both colonies have same element type, allocator type, etc. - */ -template -inline void swap( colony &a, - colony &b ) COLONY_NOEXCEPT_SWAP( - element_allocator_type ) -{ - a.swap( b ); } -} // namespace cata - -#undef COLONY_FORCE_INLINE - -#undef COLONY_NOEXCEPT_SWAP -#undef COLONY_NOEXCEPT_MOVE_ASSIGNMENT -#undef COLONY_CONSTEXPR - -#undef COLONY_CONSTRUCT -#undef COLONY_DESTROY -#undef COLONY_ALLOCATE -#undef COLONY_ALLOCATE_INITIALIZATION -#undef COLONY_DEALLOCATE +using ItemList = std::vector; #endif // CATA_SRC_COLONY_H diff --git a/src/computer_session.cpp b/src/computer_session.cpp index 4e21d7d89c99..09a4aec28fc3 100644 --- a/src/computer_session.cpp +++ b/src/computer_session.cpp @@ -232,9 +232,9 @@ static item *pick_usb() return it.typeId() == itype_usb_drive; }; - item_location loc = game_menus::inv::titled_filter_menu( filter, g->u, _( "Choose drive:" ) ); + item* loc = game_menus::inv::titled_filter_menu( filter, g->u, _( "Choose drive:" ) ); if( loc ) { - return &*loc; + return loc; } return nullptr; } @@ -370,24 +370,25 @@ void computer_session::action_sample() continue; } bool found_item = false; - item sewage( itype_sewage, calendar::turn ); - for( item &elem : here.i_at( n ) ) { - int capa = elem.get_remaining_capacity_for_liquid( sewage ); + //TODO!: check this + item *sewage = nullptr; //( itype_sewage, calendar::turn ); + for( item * const &elem : here.i_at( n ) ) { + int capa = elem->get_remaining_capacity_for_liquid( *sewage ); if( capa <= 0 ) { continue; } - capa = std::min( sewage.charges, capa ); - if( elem.contents.empty() ) { - elem.put_in( sewage ); - elem.contents.front().charges = capa; + capa = std::min( sewage->charges, capa ); + if( elem->contents.empty() ) { + elem->put_in( *sewage ); + elem->contents.front().charges = capa; } else { - elem.contents.front().charges += capa; + elem->contents.front().charges += capa; } found_item = true; break; } if( !found_item ) { - here.add_item_or_charges( n, sewage ); + here.add_item_or_charges( n, *sewage ); } } } @@ -586,10 +587,10 @@ void computer_session::action_list_bionics() int more = 0; map &here = get_map(); for( const tripoint &p : here.points_on_zlevel() ) { - for( item &elem : here.i_at( p ) ) { - if( elem.is_bionic() ) { + for( item * const &elem : here.i_at( p ) ) { + if( elem->is_bionic() ) { if( static_cast( names.size() ) < TERMY - 8 ) { - names.push_back( elem.type_name() ); + names.push_back( elem->type_name() ); } else { more++; } @@ -737,7 +738,7 @@ void computer_session::action_download_software() return; } g->u.moves -= 30; - item software( miss->get_item_id(), calendar::start_of_cataclysm ); + item &software = *item::spawn( miss->get_item_id(), calendar::start_of_cataclysm ); software.mission_id = comp.mission_id; usb->contents.clear_items(); usb->put_in( software ); @@ -777,7 +778,7 @@ void computer_session::action_blood_anal() print_line( _( "Pathogen bonded to erythrocytes and leukocytes." ) ); if( query_bool( _( "Download data?" ) ) ) { if( item *const usb = pick_usb() ) { - item software( "software_blood_data", calendar::start_of_cataclysm ); + item &software = *item::spawn( "software_blood_data", calendar::start_of_cataclysm ); usb->contents.clear_items(); usb->put_in( software ); print_line( _( "Software downloaded." ) ); @@ -815,7 +816,7 @@ void computer_session::action_data_anal() } else { // Success! if( items.only_item().typeId() == itype_black_box ) { print_line( _( "Memory Bank: Military Hexron Encryption\nPrinting Transcript\n" ) ); - item transcript( "black_box_transcript", calendar::turn ); + item &transcript = *item::spawn( "black_box_transcript", calendar::turn ); here.add_item_or_charges( g->u.pos(), transcript ); } else { print_line( _( "Memory Bank: Unencrypted\nNothing of interest.\n" ) ); @@ -1033,8 +1034,9 @@ void computer_session::action_irradiator() print_error( _( "ERROR: Processing platform empty." ) ); } else { g->u.moves -= 300; - for( auto it = here.i_at( dest ).begin(); it != here.i_at( dest ).end(); ++it ) { + for( auto iter = here.i_at( dest ).begin(); iter != here.i_at( dest ).end(); ++iter ) { // actual food processing + item *it = *iter; itype_id irradiated_type( "irradiated_" + it->typeId().str() ); if( !it->rotten() && irradiated_type.is_valid() ) { it->convert( irradiated_type ); @@ -1141,6 +1143,7 @@ void computer_session::action_geiger() // ensure only bay of each type in range void computer_session::action_conveyor() { + //TODO!: check the movement g->u.moves -= 300; tripoint loading; // red tile = loading bay tripoint unloading; // green tile = unloading bay @@ -1173,7 +1176,7 @@ void computer_session::action_conveyor() print_line( _( "No items detected at: PLATFORM." ) ); } for( const auto &it : items ) { - here.add_item_or_charges( unloading, it ); + here.add_item_or_charges( unloading, *it ); } here.i_clear( platform ); items = here.i_at( loading ); @@ -1183,8 +1186,8 @@ void computer_session::action_conveyor() print_line( _( "No items detected at: LOADING BAY." ) ); } for( const auto &it : items ) { - if( !it.made_of( LIQUID ) ) { - here.add_item_or_charges( platform, it ); + if( !it->made_of( LIQUID ) ) { + here.add_item_or_charges( platform, *it ); } } here.i_clear( loading ); diff --git a/src/condition.cpp b/src/condition.cpp index 8ab9d4db6e63..aa453debf0b2 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -760,7 +760,7 @@ void conditional_t::set_can_stow_weapon( bool is_npc ) if( is_npc ) { actor = dynamic_cast( d.beta ); } - return !actor->unarmed_attack() && actor->can_pick_volume( actor->weapon ); + return !actor->unarmed_attack() && actor->can_pick_volume( actor->get_weapon() ); }; } diff --git a/src/construction.cpp b/src/construction.cpp index d8d0bf604683..1e5d58a1d59c 100644 --- a/src/construction.cpp +++ b/src/construction.cpp @@ -1041,7 +1041,7 @@ void place_construction( const construction_group_str_id &group ) _( "There is already an unfinished construction there, examine it to continue working on it" ) ); return; } - std::list used; + ItemList used; const construction &con = *valid.find( pnt )->second; // create the partial construction struct partial_con pc; @@ -1056,8 +1056,8 @@ void place_construction( const construction_group_str_id &group ) } // Use up the components for( const auto &it : con.requirements->get_components() ) { - std::list tmp = g->u.consume_items( it, 1, is_crafting_component ); - used.splice( used.end(), tmp ); + ItemList tmp = g->u.consume_items( it, 1, is_crafting_component ); + used.insert( used.end(), tmp.begin(), tmp.end() ); } pc.components = used; here.partial_con_set( pnt, pc ); @@ -1133,7 +1133,8 @@ void complete_construction( Character &ch ) tripoint dump_spot = random_entry( dump_spots ); map_stack items = here.i_at( terp ); for( map_stack::iterator it = items.begin(); it != items.end(); ) { - here.add_item_or_charges( dump_spot, *it ); + here.add_item_or_charges( dump_spot, **it ); + //TODO!: CHECK it = items.erase( it ); } } else { @@ -1161,7 +1162,9 @@ void complete_construction( Character &ch ) // Spawn byproducts if( built.byproduct_item_group ) { - here.spawn_items( ch.pos(), item_group::items_from( built.byproduct_item_group, calendar::turn ) ); + std::vector items_list = item_group::items_from( built.byproduct_item_group, + calendar::turn ); + here.spawn_items( ch.pos(), items_list ); } add_msg( m_info, _( "%s finished construction: %s." ), ch.disp_name(), built.group->name() ); @@ -1280,10 +1283,10 @@ void construct::done_grave( const tripoint &p ) { map &here = get_map(); map_stack its = here.i_at( p ); - for( item it : its ) { - if( it.is_corpse() ) { - if( it.get_corpse_name().empty() ) { - if( it.get_mtype()->has_flag( MF_HUMAN ) ) { + for( item * const &it : its ) { + if( it->is_corpse() ) { + if( it->get_corpse_name().empty() ) { + if( it->get_mtype()->has_flag( MF_HUMAN ) ) { if( g->u.has_trait( trait_SPIRITUAL ) ) { g->u.add_morale( MORALE_FUNERAL, 50, 75, 1_days, 1_hours ); add_msg( m_good, @@ -1297,15 +1300,15 @@ void construct::done_grave( const tripoint &p ) g->u.add_morale( MORALE_FUNERAL, 50, 75, 1_days, 1_hours ); add_msg( m_good, _( "You feel sadness, but also relief after providing last rites for %s, whose name you will keep in your memory." ), - it.get_corpse_name() ); + it->get_corpse_name() ); } else { add_msg( m_neutral, _( "You bury remains of %s, who joined uncounted masses perished in the Cataclysm." ), - it.get_corpse_name() ); + it->get_corpse_name() ); } } g->events().send( - g->u.getID(), it.get_mtype()->id, it.get_corpse_name() ); + g->u.getID(), it->get_mtype()->id, it->get_corpse_name() ); } } if( g->u.has_quality( qual_CUT ) ) { @@ -1388,7 +1391,8 @@ void construct::done_deconstruct( const tripoint &p ) here.furn_set( p, f.deconstruct.furn_set ); } add_msg( _( "The %s is disassembled." ), f.name() ); - here.spawn_items( p, item_group::items_from( f.deconstruct.drop_group, calendar::turn ) ); + std::vector items_list = item_group::items_from( f.deconstruct.drop_group, calendar::turn ); + here.spawn_items( p, items_list ); // HACK: Hack alert. // Signs have cosmetics associated with them on the submap since // furniture can't store dynamic data to disk. To prevent writing @@ -1421,14 +1425,15 @@ void construct::done_deconstruct( const tripoint &p ) } here.ter_set( p, t.deconstruct.ter_set ); add_msg( _( "The %s is disassembled." ), t.name() ); - here.spawn_items( p, item_group::items_from( t.deconstruct.drop_group, calendar::turn ) ); + std::vector items_list = item_group::items_from( t.deconstruct.drop_group, calendar::turn ); + here.spawn_items( p, items_list ); } } static void unroll_digging( const int numer_of_2x4s ) { // refund components! - item rope( "rope_30" ); + item &rope = *item::spawn( "rope_30" ); map &here = get_map(); here.add_item_or_charges( g->u.pos(), rope ); // presuming 2x4 to conserve lumber. diff --git a/src/construction_partial.h b/src/construction_partial.h index e7f5bcb4b827..018b38ce67e8 100644 --- a/src/construction_partial.h +++ b/src/construction_partial.h @@ -9,7 +9,7 @@ struct partial_con { int counter = 0; - std::list components = {}; + ItemList components = {}; construction_id id = construction_id( -1 ); }; diff --git a/src/consumption.cpp b/src/consumption.cpp index 9f771866076b..50550a728d91 100644 --- a/src/consumption.cpp +++ b/src/consumption.cpp @@ -212,14 +212,14 @@ static cata::optional find_food_heater( Character &c, const inventory &inv, bool has_fire ) { if( has_fire ) { - std::vector fire_heaters = inv.items_with( []( const item & it ) { + std::vector fire_heaters = inv.items_with( []( const item & it ) { return it.has_flag( "HEATS_FOOD_USING_FIRE" ); } ); if( !fire_heaters.empty() ) { return prepared_item_consumption( item_consumption_t::none, *fire_heaters.front() ); } } - std::vector charged_heaters = inv.items_with( [&c]( const item & it ) { + std::vector charged_heaters = inv.items_with( [&c]( const item & it ) { return it.has_flag( "HEATS_FOOD_USING_CHARGES" ) && it.has_flag( "HEATS_FOOD" ) && c.has_enough_charges( it, false ); @@ -227,7 +227,7 @@ static cata::optional find_food_heater( Character &c, if( !charged_heaters.empty() ) { return prepared_item_consumption( item_consumption_t::tool, *charged_heaters.front() ); } - std::vector consumed_heaters = inv.items_with( []( const item & it ) { + std::vector consumed_heaters = inv.items_with( []( const item & it ) { return it.has_flag( "HEATS_FOOD_IS_CONSUMED" ) && it.has_flag( "HEATS_FOOD" ) && is_crafting_component( it ); @@ -334,10 +334,10 @@ nutrients Character::compute_effective_nutrients( const item &comest ) const // if item has components, will derive calories from that instead. if( !comest.components.empty() && !comest.has_flag( flag_NUTRIENT_OVERRIDE ) ) { nutrients tally{}; - for( const item &component : comest.components ) { + for( const item * const &component : comest.components ) { nutrients component_value = - compute_effective_nutrients( component ) * component.charges; - if( component.has_flag( flag_BYPRODUCT ) ) { + compute_effective_nutrients( *component ) * component->charges; + if( component->has_flag( flag_BYPRODUCT ) ) { tally -= component_value; } else { tally += component_value; @@ -403,7 +403,7 @@ std::pair Character::compute_nutrient_range( } for( const std::pair &byproduct : rec.byproducts ) { - item byproduct_it( byproduct.first, calendar::turn, byproduct.second ); + item &byproduct_it = *item::spawn_temporary( byproduct.first, calendar::turn, byproduct.second ); nutrients byproduct_nutr = compute_default_effective_nutrients( byproduct_it, *this ); tally_min -= byproduct_nutr; tally_max -= byproduct_nutr; @@ -422,8 +422,8 @@ std::pair Character::compute_nutrient_range( if( !comest->comestible ) { return {}; } - - item comest_it( comest, calendar::turn, 1 ); + //TODO!: wtf is all this shit + item &comest_it = *item::spawn_temporary( comest, calendar::turn, 1 ); // The default nutrients are always a possibility nutrients min_nutr = compute_default_effective_nutrients( comest_it, *this, extra_flags ); @@ -438,18 +438,20 @@ std::pair Character::compute_nutrient_range( nutrients this_min; nutrients this_max; - item result_it = rec->create_result(); - if( result_it.contents.num_item_stacks() == 1 ) { - const item alt_result = result_it.contents.front(); + item *result_it = &rec->create_result(); + if( result_it->contents.num_item_stacks() == 1 ) { + item &alt_result = result_it->contents.front(); if( alt_result.typeId() == comest_it.typeId() ) { - result_it = alt_result; + result_it->contents.remove_top( &alt_result ); + result_it->destroy(); + result_it = &alt_result; } } - if( result_it.typeId() != comest_it.typeId() ) { + if( result_it->typeId() != comest_it.typeId() ) { debugmsg( "When creating recipe result expected %s, got %s\n", - comest_it.typeId().str(), result_it.typeId().str() ); + comest_it.typeId().str(), result_it->typeId().str() ); } - std::tie( this_min, this_max ) = compute_nutrient_range( result_it, rec, extra_flags ); + std::tie( this_min, this_max ) = compute_nutrient_range( *result_it, rec, extra_flags ); min_nutr.min_in_place( this_min ); max_nutr.max_in_place( this_max ); } @@ -1507,10 +1509,7 @@ item &Character::get_consumable_from( item &it ) const return it; } - static item null_comestible; - // Since it's not const. - null_comestible = item(); - return null_comestible; + return null_item_reference(); } consumption_event::consumption_event( const item &food ) : time( calendar::turn ) diff --git a/src/craft_command.cpp b/src/craft_command.cpp index 79eb365f1f19..2d02ef33f85e 100644 --- a/src/craft_command.cpp +++ b/src/craft_command.cpp @@ -219,14 +219,15 @@ bool craft_command::query_continue( const std::vector> return query_yn( ss ); } - -item craft_command::create_in_progress_craft() +//TODO!: restore this +item &craft_command::create_in_progress_craft() { + /* // Use up the components and tools - std::list used; + ItemList used; std::vector comps_used; if( crafter->has_trait( trait_DEBUG_HS ) ) { - item new_craft( rec, batch_size, used, comps_used ); + item* new_craft=item::create( rec, batch_size, used, comps_used ); new_craft.set_tools_to_continue( true ); return new_craft; } @@ -247,7 +248,7 @@ item craft_command::create_in_progress_craft() const auto filter = rec->get_component_filter( flags ); for( const auto &it : item_selections ) { - std::list tmp = crafter->consume_items( it, batch_size, filter ); + ItemList tmp = crafter->consume_items( it, batch_size, filter ); used.splice( used.end(), tmp ); } @@ -277,6 +278,8 @@ item craft_command::create_in_progress_craft() new_craft.set_next_failure_point( *crafter ); return new_craft; + */ + return *item::spawn(); } skill_id craft_command::get_skill_id() diff --git a/src/craft_command.h b/src/craft_command.h index 08d200f6e293..836512bcfdfa 100644 --- a/src/craft_command.h +++ b/src/craft_command.h @@ -77,7 +77,7 @@ class craft_command * Consumes the selected components and returns the resulting in progress craft item. * Must be called after execute(). */ - item create_in_progress_craft(); + item &create_in_progress_craft(); bool is_long() const { return longcraft; diff --git a/src/crafting.cpp b/src/crafting.cpp index a8b3329bdda5..d4f74b61f47d 100644 --- a/src/crafting.cpp +++ b/src/crafting.cpp @@ -413,41 +413,41 @@ int player::expected_time_to_craft( const recipe &rec, int batch_size, bool in_p bool player::check_eligible_containers_for_crafting( const recipe &rec, int batch_size ) const { std::vector conts = get_eligible_containers_for_crafting(); - const std::vector res = rec.create_results( batch_size ); - const std::vector bps = rec.create_byproducts( batch_size ); - std::vector all; + const std::vector res = rec.create_results( batch_size ); + const std::vector bps = rec.create_byproducts( batch_size ); + std::vector all; all.reserve( res.size() + bps.size() ); all.insert( all.end(), res.begin(), res.end() ); all.insert( all.end(), bps.begin(), bps.end() ); map &here = get_map(); - for( const item &prod : all ) { - if( !prod.made_of( LIQUID ) ) { + for( const item * const &prod : all ) { + if( !prod->made_of( LIQUID ) ) { continue; } // we go through half-filled containers first, then go through empty containers if we need std::sort( conts.begin(), conts.end(), item_ptr_compare_by_charges ); - int charges_to_store = prod.charges; + int charges_to_store = prod->charges; for( const item *cont : conts ) { if( charges_to_store <= 0 ) { break; } if( !cont->is_container_empty() ) { - if( cont->contents.front().typeId() == prod.typeId() ) { + if( cont->contents.front().typeId() == prod->typeId() ) { charges_to_store -= cont->get_remaining_capacity_for_liquid( cont->contents.front(), true ); } } else { - charges_to_store -= cont->get_remaining_capacity_for_liquid( prod, true ); + charges_to_store -= cont->get_remaining_capacity_for_liquid( *prod, true ); } } // also check if we're currently in a vehicle that has the necessary storage if( charges_to_store > 0 ) { if( optional_vpart_position vp = here.veh_at( pos() ) ) { - const itype_id &ftype = prod.typeId(); + const itype_id &ftype = prod->typeId(); int fuel_cap = vp->vehicle().fuel_capacity( ftype ); int fuel_amnt = vp->vehicle().fuel_left( ftype ); @@ -461,7 +461,7 @@ bool player::check_eligible_containers_for_crafting( const recipe &rec, int batc if( charges_to_store > 0 ) { if( !query_yn( _( "You don't have anything in which to store %s and may have to pour it out or consume it as soon as it is prepared! Proceed?" ), - prod.tname() ) ) { + prod->tname() ) ) { return false; } } @@ -483,18 +483,18 @@ std::vector player::get_eligible_containers_for_crafting() const { std::vector conts; - if( is_container_eligible_for_crafting( weapon, true ) ) { - conts.push_back( &weapon ); + if( is_container_eligible_for_crafting( get_weapon(), true ) ) { + conts.push_back( &get_weapon() ); } for( const auto &it : worn ) { - if( is_container_eligible_for_crafting( it, false ) ) { - conts.push_back( &it ); + if( is_container_eligible_for_crafting( *it, false ) ) { + conts.push_back( it ); } } for( size_t i = 0; i < inv.size(); i++ ) { for( const auto &it : inv.const_stack( i ) ) { - if( is_container_eligible_for_crafting( it, false ) ) { - conts.push_back( &it ); + if( is_container_eligible_for_crafting( *it, false ) ) { + conts.push_back( it ); } } } @@ -508,8 +508,8 @@ std::vector player::get_eligible_containers_for_crafting() const } if( here.accessible_items( loc ) ) { for( const auto &it : here.i_at( loc ) ) { - if( is_container_eligible_for_crafting( it, true ) ) { - conts.emplace_back( &it ); + if( is_container_eligible_for_crafting( *it, true ) ) { + conts.emplace_back( it ); } } } @@ -517,8 +517,8 @@ std::vector player::get_eligible_containers_for_crafting() const if( const cata::optional vp = here.veh_at( loc ).part_with_feature( "CARGO", true ) ) { for( const auto &it : vp->vehicle().get_items( vp->part_index() ) ) { - if( is_container_eligible_for_crafting( it, false ) ) { - conts.emplace_back( &it ); + if( is_container_eligible_for_crafting( *it, false ) ) { + conts.emplace_back( it ); } } } @@ -569,19 +569,21 @@ const inventory &Character::crafting_inventory( const tripoint &src_pos, int rad } cached_crafting_inventory.form_from_map( inv_pos, radius, this, false, clear_path ); cached_crafting_inventory += inv; - cached_crafting_inventory += weapon; + cached_crafting_inventory += *weapon; cached_crafting_inventory += worn; for( const bionic &bio : *my_bionics ) { const bionic_data &bio_data = bio.info(); if( ( !bio_data.activated || bio.powered ) && !bio_data.fake_item.is_empty() ) { - cached_crafting_inventory += item( bio.info().fake_item, - calendar::turn, units::to_kilojoule( get_power_level() ) ); + //TODO!: restore next + //cached_crafting_inventory += item( bio.info().fake_item, + // calendar::turn, units::to_kilojoule( get_power_level() ) ); } } if( has_trait( trait_BURROW ) ) { - cached_crafting_inventory += item( "pickaxe", calendar::turn ); - cached_crafting_inventory += item( "shovel", calendar::turn ); + //TODO!: restore next two + //cached_crafting_inventory += item( "pickaxe", calendar::turn ); + //cached_crafting_inventory += item( "shovel", calendar::turn ); } cached_moves = moves; @@ -623,9 +625,10 @@ void player::make_craft_with_command( const recipe_id &id_to_make, int batch_siz // @param offset is the index of the created item in the range [0, batch_size-1], // it makes sure that the used items are distributed equally among the new items. -static void set_components( std::list &components, const std::list &used, +static void set_components( std::vector < item * > &components, const ItemList &used, const int batch_size, const size_t offset ) { + //TODO!: check? if( batch_size <= 1 ) { components.insert( components.begin(), used.begin(), used.end() ); return; @@ -633,11 +636,11 @@ static void set_components( std::list &components, const std::list & // This count does *not* include items counted by charges! size_t non_charges_counter = 0; for( auto &tmp : used ) { - if( tmp.count_by_charges() ) { + if( tmp->count_by_charges() ) { components.push_back( tmp ); // This assumes all (count-by-charges) items of the same type have been merged into one, // which has a charges value that can be evenly divided by batch_size. - components.back().charges = tmp.charges / batch_size; + components.back()->charges = tmp->charges / batch_size; } else { if( ( non_charges_counter + offset ) % batch_size == 0 ) { components.push_back( tmp ); @@ -651,24 +654,24 @@ static void set_components( std::list &components, const std::list & * Helper for @ref set_item_map_or_vehicle * This is needed to still get a vaild item_location if overflow occurs */ -static item_location set_item_map( const tripoint &loc, item &newit ) +static item* set_item_map( const tripoint &loc, item &newit ) { // Includes loc for( const tripoint &tile : closest_points_first( loc, 2 ) ) { // Pass false to disallow overflow, null_item_reference indicates failure. item *it_on_map = &get_map().add_item_or_charges( tile, newit, false ); if( it_on_map != &null_item_reference() ) { - return item_location( map_cursor( tile ), it_on_map ); + return it_on_map; } } debugmsg( "Could not place %s on map near (%d, %d, %d)", newit.tname(), loc.x, loc.y, loc.z ); - return item_location(); + return nullptr; } /** * Set an item on the map or in a vehicle and return the new location */ -static item_location set_item_map_or_vehicle( const player &p, const tripoint &loc, item &newit ) +static item* set_item_map_or_vehicle( const player &p, const tripoint &loc, item &newit ) { map &here = get_map(); if( const cata::optional vp = here.veh_at( loc ).part_with_feature( "CARGO", @@ -679,9 +682,9 @@ static item_location set_item_map_or_vehicle( const player &p, const tripoint &l p.add_msg_player_or_npc( pgettext( "item, furniture", "You put the %1$s on the %2$s." ), pgettext( "item, furniture", " puts the %1$s on the %2$s." ), - ( *it )->tname(), vp->part().name() ); + ( **it )->tname(), vp->part().name() ); - return item_location( vehicle_cursor( vp->vehicle(), vp->part_index() ), & **it ); + return **it; } // Couldn't add the in progress craft to the target part, so drop it to the map. @@ -709,7 +712,7 @@ static item_location set_item_map_or_vehicle( const player &p, const tripoint &l } } -static item_location set_item_inventory( player &p, item &newit ) +static item* set_item_inventory( player &p, item &newit ) { p.inv.assign_empty_invlet( newit, p ); // We might not have space for the item @@ -718,20 +721,20 @@ static item_location set_item_inventory( player &p, item &newit ) item &item_in_inv = p.i_add( newit ); add_msg( m_info, "%c - %s", item_in_inv.invlet == 0 ? ' ' : item_in_inv.invlet, item_in_inv.tname() ); - return item_location( p, &item_in_inv ); + return &item_in_inv; } return set_item_map_or_vehicle( p, p.pos(), newit ); } -item_location player::start_craft( craft_command &command, const tripoint & ) +item* player::start_craft( craft_command &command, const tripoint & ) { if( command.empty() ) { debugmsg( "Attempted to start craft with empty command" ); - return item_location(); + return nullptr; } - item craft = command.create_in_progress_craft(); + item &craft = command.create_in_progress_craft(); const recipe &making = craft.get_making(); if( get_skill_level( command.get_skill_id() ) > making.difficulty * 1.25 ) { handle_skill_warning( command.get_skill_id(), true ); @@ -752,7 +755,7 @@ item_location player::start_craft( craft_command &command, const tripoint & ) // Regardless of whether a workbench exists or not, // we still craft in inventory or under player, because QoL. - item_location craft_in_world = set_item_inventory( *this, craft ); + item* craft_in_world = set_item_inventory( *this, craft ); assign_activity( ACT_CRAFT ); activity.targets.push_back( craft_in_world ); @@ -918,10 +921,12 @@ static void destroy_random_component( item &craft, const player &crafter ) return; } - item destroyed = random_entry_removed( craft.components ); + item *destroyed = random_entry_removed( craft.components ); crafter.add_msg_player_or_npc( _( "You mess up and destroy the %s." ), - _( " messes up and destroys the %s" ), destroyed.tname() ); + _( " messes up and destroys the %s" ), destroyed->tname() ); + + //TODO!: check, destroy for real } bool item::handle_craft_failure( player &crafter ) @@ -1006,10 +1011,10 @@ void item::inherit_flags( const item &parent, const recipe &making ) } } -void item::inherit_flags( const std::list &parents, const recipe &making ) +void item::inherit_flags( const ItemList &parents, const recipe &making ) { - for( const item &parent : parents ) { - inherit_flags( parent, making ); + for( const item * const &parent : parents ) { + inherit_flags( *parent, making ); } } @@ -1022,22 +1027,22 @@ void complete_craft( player &p, item &craft, const bench_location & ) const recipe &making = craft.get_making(); const int batch_size = craft.charges; - std::list &used = craft.components; + ItemList &used = craft.components; const double relative_rot = craft.get_relative_rot(); // Set up the new item, and assign an inventory letter if available - std::vector newits = making.create_results( batch_size ); + std::vector newits = making.create_results( batch_size ); const bool should_heat = making.hot_result(); bool first = true; size_t newit_counter = 0; - for( item &newit : newits ) { + for( item * const &newit : newits ) { // Points to newit unless newit is a non-empty container, then it points to newit's contents. // Necessary for things like canning soup; sometimes we want to operate on the soup, not the can. - item &food_contained = ( newit.is_container() && !newit.contents.empty() ) ? - newit.contents.back() : newit; + item &food_contained = ( newit->is_container() && !newit->contents.empty() ) ? + newit->contents.back() : *newit; // messages, learning of recipe, food spoilage calculation only once if( first ) { @@ -1085,23 +1090,24 @@ void complete_craft( player &p, item &craft, const bench_location & ) newit_counter++; } else if( food_contained.is_food() && !food_contained.has_flag( flag_NUTRIENT_OVERRIDE ) ) { // if a component item has "cooks_like" it will be replaced by that item as a component - for( item &comp : used ) { + for( item * const &comp : used ) { // only comestibles have cooks_like. any other type of item will throw an exception, so filter those out - if( comp.is_comestible() && !comp.get_comestible()->cooks_like.is_empty() ) { - comp = item( comp.get_comestible()->cooks_like, comp.birthday(), comp.charges ); + if( comp->is_comestible() && !comp->get_comestible()->cooks_like.is_empty() ) { + //TODO!: restore next + //comp = item( comp.get_comestible()->cooks_like, comp->birthday(), comp->charges ); } // If this recipe is cooked, components are no longer raw. if( should_heat ) { - comp.set_flag_recursive( flag_COOKED ); + comp->set_flag_recursive( flag_COOKED ); } } // use a copy of the used list so that the byproducts don't build up over iterations (#38071) - std::list usedbp = used; + ItemList usedbp = used; // byproducts get stored as a "component" but with a byproduct flag for consumption purposes if( making.has_byproducts() ) { - for( item &byproduct : making.create_byproducts( batch_size ) ) { - byproduct.set_flag( flag_BYPRODUCT ); + for( item * const &byproduct : making.create_byproducts( batch_size ) ) { + byproduct->set_flag( flag_BYPRODUCT ); usedbp.push_back( byproduct ); } } @@ -1109,11 +1115,12 @@ void complete_craft( player &p, item &craft, const bench_location & ) set_components( food_contained.components, usedbp, batch_size, newit_counter ); // store the number of charges the recipe would create with batch size 1. - if( &newit != &food_contained ) { // If a canned/contained item was crafted… + //TODO!: check what ref level should be compared here + if( newit != &food_contained ) { // If a canned/contained item was crafted… // … the container holds exactly one completion of the recipe, no matter the batch size. food_contained.recipe_charges = food_contained.charges; } else { // Otherwise, the item is already stacked so we need to divide by batch size. - newit.recipe_charges = newit.charges / batch_size; + newit->recipe_charges = newit->charges / batch_size; } newit_counter++; } @@ -1122,30 +1129,31 @@ void complete_craft( player &p, item &craft, const bench_location & ) food_contained.set_relative_rot( relative_rot ); } - newit.set_owner( p.get_faction()->id ); + newit->set_owner( p.get_faction()->id ); // If these aren't equal, newit is a container, so finalize its contents too. - if( &newit != &food_contained ) { + //TODO!: same as above + if( newit != &food_contained ) { food_contained.set_owner( p.get_faction()->id ); } - if( newit.made_of( LIQUID ) ) { - liquid_handler::handle_all_liquid( newit, PICKUP_RANGE ); + if( newit->made_of( LIQUID ) ) { + liquid_handler::handle_all_liquid( *newit, PICKUP_RANGE ); } else { - set_item_inventory( p, newit ); + set_item_inventory( p, *newit ); } } if( making.has_byproducts() ) { - std::vector bps = making.create_byproducts( batch_size ); + std::vector bps = making.create_byproducts( batch_size ); for( auto &bp : bps ) { - if( bp.goes_bad() ) { - bp.set_relative_rot( relative_rot ); + if( bp->goes_bad() ) { + bp->set_relative_rot( relative_rot ); } - bp.set_owner( p.get_faction()->id ); - if( bp.made_of( LIQUID ) ) { - liquid_handler::handle_all_liquid( bp, PICKUP_RANGE ); + bp->set_owner( p.get_faction()->id ); + if( bp->made_of( LIQUID ) ) { + liquid_handler::handle_all_liquid( *bp, PICKUP_RANGE ); } else { - set_item_inventory( p, bp ); + set_item_inventory( p, *bp ); } } } @@ -1215,7 +1223,8 @@ bool player::can_continue_craft( item &craft ) item_selections.push_back( is ); } for( const auto &it : item_selections ) { - craft.components.splice( craft.components.end(), consume_items( it, batch_size, filter ) ); + ItemList items = consume_items( it, batch_size, filter ); + craft.components.insert( craft.components.end(), items.begin(), items.end() ); } } @@ -1475,14 +1484,13 @@ comp_selection player::select_item_component( const std::vectori_add_or_drop( tmp ); + who.as_player()->i_add_or_drop( newit ); } } @@ -1492,29 +1500,29 @@ static void empty_buckets( player &p ) { // First grab (remove) all items that are non-empty buckets and not wielded auto buckets = p.remove_items_with( [&p]( const item & it ) { - return it.is_bucket_nonempty() && &it != &p.weapon; + return it.is_bucket_nonempty() && &it != &p.get_weapon(); }, INT_MAX ); for( auto &it : buckets ) { - for( const item *in : it.contents.all_items_top() ) { + for( item *&in : it->contents.all_items_top() ) { drop_or_handle( *in, p ); } - it.contents.clear_items(); - drop_or_handle( it, p ); + it->contents.clear_items(); + drop_or_handle( *it, p ); } } -std::list player::consume_items( const comp_selection &is, int batch, - const std::function &filter ) +ItemList player::consume_items( const comp_selection &is, int batch, + const std::function &filter ) { return consume_items( get_map(), is, batch, filter, pos(), PICKUP_RANGE ); } -std::list player::consume_items( map &m, const comp_selection &is, int batch, - const std::function &filter, - const tripoint &origin, int radius ) +ItemList player::consume_items( map &m, const comp_selection &is, int batch, + const std::function &filter, + const tripoint &origin, int radius ) { - std::list ret; + ItemList ret; if( has_trait( trait_DEBUG_HS ) ) { return ret; @@ -1530,30 +1538,30 @@ std::list player::consume_items( map &m, const comp_selection & // First try to get everything from the map, than (remaining amount) from player if( is.use_from & use_from_map ) { if( by_charges ) { - std::list tmp = m.use_charges( loc, radius, selected_comp.type, real_count, filter ); - ret.splice( ret.end(), tmp ); + ItemList tmp = m.use_charges( loc, radius, selected_comp.type, real_count, filter ); + ret.insert( ret.end(), tmp.begin(), tmp.end() ); } else { - std::list tmp = g->m.use_amount( loc, radius, selected_comp.type, real_count, filter ); + ItemList tmp = g->m.use_amount( loc, radius, selected_comp.type, real_count, filter ); remove_ammo( tmp, *this ); - ret.splice( ret.end(), tmp ); + ret.insert( ret.end(), tmp.begin(), tmp.end() ); } } if( is.use_from & use_from_player ) { if( by_charges ) { - std::list tmp = use_charges( selected_comp.type, real_count, filter ); - ret.splice( ret.end(), tmp ); + ItemList tmp = use_charges( selected_comp.type, real_count, filter ); + ret.insert( ret.end(), tmp.begin(), tmp.end() ); } else { - std::list tmp = use_amount( selected_comp.type, real_count, filter ); + ItemList tmp = use_amount( selected_comp.type, real_count, filter ); remove_ammo( tmp, *this ); - ret.splice( ret.end(), tmp ); + ret.insert( ret.end(), tmp.begin(), tmp.end() ); } } // condense those items into one if( by_charges && ret.size() > 1 ) { - std::list::iterator b = ret.begin(); + ItemList::iterator b = ret.begin(); b++; while( ret.size() > 1 ) { - ret.front().charges += b->charges; + ret.front()->charges += ( *b )->charges; b = ret.erase( b ); } } @@ -1565,8 +1573,8 @@ std::list player::consume_items( map &m, const comp_selection & /* This call is in-efficient when doing it for multiple items with the same map inventory. In that case, consider using select_item_component with 1 pre-created map inventory, and then passing the results to consume_items */ -std::list player::consume_items( const std::vector &components, int batch, - const std::function &filter ) +ItemList player::consume_items( const std::vector &components, int batch, + const std::function &filter ) { inventory map_inv; map_inv.form_from_map( pos(), PICKUP_RANGE, this ); @@ -2047,7 +2055,7 @@ static disass_prompt_result prompt_disassemble_in_seq( avatar &you, const item & return res; } -static bool prompt_disassemble_single( avatar &you, item_location target, bool interactive ) +static bool prompt_disassemble_single( avatar &you, item* target, bool interactive ) { if( !target ) { add_msg( _( "Never mind." ) ); @@ -2073,13 +2081,13 @@ static bool prompt_disassemble_single( avatar &you, item_location target, bool i bool crafting::disassemble( avatar &you ) { - item_location target = game_menus::inv::disassemble( you ); + item* target = game_menus::inv::disassemble( you ); return prompt_disassemble_single( you, target, false ); } -bool crafting::disassemble( avatar &you, item_location target ) +bool crafting::disassemble( avatar &you, item& target ) { - return prompt_disassemble_single( you, target, true ); + return prompt_disassemble_single( you, &target, true ); } bool crafting::disassemble_all( avatar &you, bool recursively ) @@ -2088,11 +2096,11 @@ bool crafting::disassemble_all( avatar &you, bool recursively ) tripoint pos = you.pos(); - for( item &itm : get_map().i_at( pos ) ) { - disass_prompt_result res = prompt_disassemble_in_seq( you, itm, false, true ); + for( item * const &itm : get_map().i_at( pos ) ) { + disass_prompt_result res = prompt_disassemble_in_seq( you, *itm, false, true ); if( res.success ) { iuse_location loc; - loc.loc = item_location( map_cursor( pos ), &itm ); + loc.loc = itm; loc.count = res.batches ? *res.batches : 1; targets.push_back( std::move( loc ) ); } @@ -2108,8 +2116,11 @@ bool crafting::disassemble_all( avatar &you, bool recursively ) } } -void crafting::complete_disassemble( Character &who, iuse_location target, const tripoint &/*pos*/ ) +void crafting::complete_disassemble( Character &/*who*/, iuse_location /*target*/, + const tripoint &/*pos*/ ) { + /* + //TODO!: NO DON't lol. whole function needs the big restore item &org_item = *target.loc; const recipe &dis = recipe_dictionary::get_uncraft( org_item.typeId() ); @@ -2119,165 +2130,166 @@ void crafting::complete_disassemble( Character &who, iuse_location target, const // Make a copy to keep its data (damage/components) even after it // has been removed. - item dis_item = org_item; - - float component_success_chance = std::min( std::pow( 0.8, dis_item.damage_level( 4 ) ), 1.0 ); + item dis_item = &org_item; + float component_success_chance = std::min( std::pow( 0.8, dis_item.damage_level( 4 ) ), 1.0 ); - add_msg( _( "You disassemble the %s into its components." ), dis_item.tname() ); - // Remove any batteries, ammo and mods first - remove_ammo( dis_item, who ); - remove_radio_mod( dis_item, *who.as_player() ); + add_msg( _( "You disassemble the %s into its components." ), dis_item.tname() ); + // Remove any batteries, ammo and mods first + remove_ammo( dis_item, who ); + remove_radio_mod( dis_item, *who.as_player() ); - if( org_item.count_by_charges() ) { - int batch_size = dis.disassembly_batch_size(); - org_item.charges -= batch_size * target.count; - } - // remove the item, except when it's counted by charges and still has some - if( !org_item.count_by_charges() || org_item.charges <= 0 ) { - target.loc.remove_item(); - } + if( org_item.count_by_charges() ) { + int batch_size = dis.disassembly_batch_size(); + org_item.charges -= batch_size * target.count; + } + // remove the item, except when it's counted by charges and still has some + if( !org_item.count_by_charges() || org_item.charges <= 0 ) { + target.loc.remove_item(); + } - // Consume tool charges - for( const auto &it : dis_requirements.get_tools() ) { - who.as_player()->consume_tools( it ); - } + // Consume tool charges + for( const auto &it : dis_requirements.get_tools() ) { + who.as_player()->consume_tools( it ); + } - // add the components to the map - // Player skills should determine how many components are returned + // add the components to the map + // Player skills should determine how many components are returned - int skill_dice = 2 + who.get_skill_level( dis.skill_used ) * 3; - skill_dice += who.get_skill_level( dis.skill_used ); + int skill_dice = 2 + who.get_skill_level( dis.skill_used ) * 3; + skill_dice += who.get_skill_level( dis.skill_used ); - // Sides on dice is 16 plus your current intelligence - ///\EFFECT_INT increases success rate for disassembling items - int skill_sides = 16 + who.int_cur; - - int diff_dice = dis.difficulty; - int diff_sides = 24; // 16 + 8 (default intelligence) - - // disassembly only nets a bit of practice - if( dis.skill_used ) { - who.as_player()->practice( dis.skill_used, ( dis.difficulty ) * 2, dis.difficulty ); - } - - // If the components aren't empty, we want items exactly identical to them - // Even if the best-fit recipe does not involve those items - std::list components = dis_item.components; - - // If the components are empty, item is the default kind and made of default components - if( components.empty() ) { - const bool uncraft_liquids_contained = dis.has_flag( flag_UNCRAFT_LIQUIDS_CONTAINED ); - for( const auto &altercomps : dis_requirements.get_components() ) { - const item_comp &comp = altercomps.front(); - int compcount = comp.count * target.count; - item newit( comp.type, calendar::turn ); - const bool is_liquid = newit.made_of( LIQUID ); - if( uncraft_liquids_contained && is_liquid && newit.charges != 0 ) { - // Spawn liquid item in its default container - compcount = compcount / newit.charges; - if( compcount != 0 ) { - newit = newit.in_its_container(); - } - } else { - // Compress liquids and counted-by-charges items into one item, - // they are added together on the map anyway and handle_liquid - // should only be called once to put it all into a container at once. - if( newit.count_by_charges() || is_liquid ) { - newit.charges = compcount; - compcount = 1; - } else if( !newit.craft_has_charges() && newit.charges > 0 ) { - // tools that can be unloaded should be created unloaded, - // tools that can't be unloaded will keep their default charges. - newit.charges = 0; - } - } + // Sides on dice is 16 plus your current intelligence + ///\EFFECT_INT increases success rate for disassembling items + int skill_sides = 16 + who.int_cur; - // If the recipe has a `FULL_MAGAZINE` flag, spawn any magazines full of ammo - if( newit.is_magazine() && dis.has_flag( flag_FULL_MAGAZINE ) ) { - newit.ammo_set( newit.ammo_default(), newit.ammo_capacity() ); - } + int diff_dice = dis.difficulty; + int diff_sides = 24; // 16 + 8 (default intelligence) - for( ; compcount > 0; compcount-- ) { - components.emplace_back( newit ); - } + // disassembly only nets a bit of practice + if( dis.skill_used ) { + who.as_player()->practice( dis.skill_used, ( dis.difficulty ) * 2, dis.difficulty ); } - } - std::list drop_items; + // If the components aren't empty, we want items exactly identical to them + // Even if the best-fit recipe does not involve those items + ItemList components = dis_item.components; - for( const item &newit : components ) { - const bool comp_success = ( dice( skill_dice, skill_sides ) > dice( diff_dice, diff_sides ) ); - if( dis.difficulty != 0 && !comp_success ) { - add_msg( m_bad, _( "You fail to recover %s." ), newit.tname() ); - continue; - } - const bool dmg_success = component_success_chance > rng_float( 0, 1 ); - if( !dmg_success ) { - // Show reason for failure (damaged item, tname contains the damage adjective) - //~ %1s - material, %2$s - disassembled item - add_msg( m_bad, _( "You fail to recover %1$s from the %2$s." ), newit.tname(), - dis_item.tname() ); - continue; - } - // Use item from components list, or (if not contained) - // use newit, the default constructed. - item act_item = newit; - - // Refitted clothing disassembles into refitted components (when applicable) - if( dis_item.has_flag( flag_FIT ) && act_item.has_flag( flag_VARSIZE ) ) { - act_item.set_flag( flag_FIT ); - } + // If the components are empty, item is the default kind and made of default components + if( components.empty() ) { + const bool uncraft_liquids_contained = dis.has_flag( flag_UNCRAFT_LIQUIDS_CONTAINED ); + for( const auto &altercomps : dis_requirements.get_components() ) { + const item_comp &comp = altercomps.front(); + int compcount = comp.count * target.count; + item newit( comp.type, calendar::turn ); + const bool is_liquid = newit.made_of( LIQUID ); + if( uncraft_liquids_contained && is_liquid && newit.charges != 0 ) { + // Spawn liquid item in its default container + compcount = compcount / newit.charges; + if( compcount != 0 ) { + newit = newit.in_its_container(); + } + } else { + // Compress liquids and counted-by-charges items into one item, + // they are added together on the map anyway and handle_liquid + // should only be called once to put it all into a container at once. + if( newit.count_by_charges() || is_liquid ) { + newit.charges = compcount; + compcount = 1; + } else if( !newit.craft_has_charges() && newit.charges > 0 ) { + // tools that can be unloaded should be created unloaded, + // tools that can't be unloaded will keep their default charges. + newit.charges = 0; + } + } - if( filthy ) { - act_item.set_flag( "FILTHY" ); - } + // If the recipe has a `FULL_MAGAZINE` flag, spawn any magazines full of ammo + if( newit.is_magazine() && dis.has_flag( flag_FULL_MAGAZINE ) ) { + newit.ammo_set( newit.ammo_default(), newit.ammo_capacity() ); + } - for( std::list::iterator a = dis_item.components.begin(); a != dis_item.components.end(); - ++a ) { - if( a->type == newit.type ) { - act_item = *a; - dis_item.components.erase( a ); - break; + for( ; compcount > 0; compcount-- ) { + components.emplace_back( newit ); + } } } - if( act_item.made_of( LIQUID ) ) { - liquid_handler::handle_all_liquid( act_item, PICKUP_RANGE ); - } else { - drop_items.push_back( act_item ); - } - } + ItemList drop_items; - put_into_vehicle_or_drop( who, item_drop_reason::deliberate, drop_items ); - - if( !dis.learn_by_disassembly.empty() && !who.knows_recipe( &dis ) ) { - if( who.can_learn_by_disassembly( dis ) ) { - // TODO: make this depend on intelligence - if( one_in( 4 ) ) { - // TODO: change to forward an id or a reference - who.learn_recipe( &dis.ident().obj() ); - add_msg( m_good, _( "You learned a recipe for %s from disassembling it!" ), + for( const item * const &newit : components ) { + const bool comp_success = ( dice( skill_dice, skill_sides ) > dice( diff_dice, diff_sides ) ); + if( dis.difficulty != 0 && !comp_success ) { + add_msg( m_bad, _( "You fail to recover %s." ), newit->tname() ); + continue; + } + const bool dmg_success = component_success_chance > rng_float( 0, 1 ); + if( !dmg_success ) { + // Show reason for failure (damaged item, tname contains the damage adjective) + //~ %1s - material, %2$s - disassembled item + add_msg( m_bad, _( "You fail to recover %1$s from the %2$s." ), newit->tname(), dis_item.tname() ); + continue; + } + // Use item from components list, or (if not contained) + // use newit, the default constructed. + item act_item = newit; + + // Refitted clothing disassembles into refitted components (when applicable) + if( dis_item.has_flag( flag_FIT ) && act_item.has_flag( flag_VARSIZE ) ) { + act_item.set_flag( flag_FIT ); + } + + if( filthy ) { + act_item.set_flag( "FILTHY" ); + } + + for( ItemList::iterator a = dis_item.components.begin(); a != dis_item.components.end(); + ++a ) { + if( a->type == newit.type ) { + act_item = *a; + dis_item.components.erase( a ); + break; + } + } + + if( act_item.made_of( LIQUID ) ) { + liquid_handler::handle_all_liquid( act_item, PICKUP_RANGE ); + } else { + drop_items.push_back( act_item ); + } + } + + put_into_vehicle_or_drop( who, item_drop_reason::deliberate, drop_items ); + + if( !dis.learn_by_disassembly.empty() && !who.knows_recipe( &dis ) ) { + if( who.can_learn_by_disassembly( dis ) ) { + // TODO: make this depend on intelligence + if( one_in( 4 ) ) { + // TODO: change to forward an id or a reference + who.learn_recipe( &dis.ident().obj() ); + add_msg( m_good, _( "You learned a recipe for %s from disassembling it!" ), + dis_item.tname() ); + } else { + add_msg( m_info, _( "You might be able to learn a recipe for %s if you disassemble another." ), + dis_item.tname() ); + } } else { - add_msg( m_info, _( "You might be able to learn a recipe for %s if you disassemble another." ), - dis_item.tname() ); + add_msg( m_info, _( "If you had better skills, you might learn a recipe next time." ) ); } - } else { - add_msg( m_info, _( "If you had better skills, you might learn a recipe next time." ) ); - } - } + }*/ } -void remove_ammo( std::list &dis_items, Character &who ) +void remove_ammo( ItemList &dis_items, Character &who ) { for( auto &dis_item : dis_items ) { - remove_ammo( dis_item, who ); + remove_ammo( *dis_item, who ); } } -void remove_ammo( item &dis_item, Character &who ) +void remove_ammo( item &/*dis_item*/, Character &/*who*/ ) { - dis_item.remove_items_with( [&who]( const item & it ) { + //TODO!: check for ownership and restore + /* + dis_item.remove_items_with( [&who]( item & it ) { if( it.is_irremovable() ) { return false; } @@ -2302,7 +2314,7 @@ void remove_ammo( item &dis_item, Character &who ) } drop_or_handle( ammodrop, who ); dis_item.charges = 0; - } + }*/ } std::vector player::get_crafting_helpers( size_t max ) const diff --git a/src/crafting.h b/src/crafting.h index 5df0b345a967..79062005334e 100644 --- a/src/crafting.h +++ b/src/crafting.h @@ -9,12 +9,12 @@ #include "point.h" #include "ret_val.h" #include "type_id.h" +#include "colony.h" class avatar; class Character; class inventory; class item; -class item_location; class player; class recipe; struct iuse_location; @@ -47,7 +47,7 @@ void remove_ammo( item &dis_item, Character &who ); /** * @brief Removes any (removable) ammo from each item and stores it in character's inventory. */ -void remove_ammo( std::list &dis_items, Character &who ); +void remove_ammo( ItemList &dis_items, Character &who ); bench_location find_best_bench( const player &p, const item &craft ); @@ -114,7 +114,7 @@ bool disassemble( avatar &you ); /** * Prompt to disassemble given item, then start activity. */ -bool disassemble( avatar &you, item_location target ); +bool disassemble( avatar &you, item &target ); /** * Start an activity to disassemble all items in avatar's square. diff --git a/src/crafting_gui.cpp b/src/crafting_gui.cpp index cc5e415f4471..284008fd8dfe 100644 --- a/src/crafting_gui.cpp +++ b/src/crafting_gui.cpp @@ -162,7 +162,7 @@ const recipe *select_crafting_recipe( int &batch_size ) { struct { const recipe *last_recipe = nullptr; - item dummy; + item *dummy = &null_item_reference(); } item_info_cache; int item_info_scroll = 0; int item_info_scroll_popup = 0; @@ -171,15 +171,15 @@ const recipe *select_crafting_recipe( int &batch_size ) [&]( const recipe * rec, const int count, int &scroll_pos ) { if( item_info_cache.last_recipe != rec ) { item_info_cache.last_recipe = rec; - item_info_cache.dummy = rec->create_result(); - item_info_cache.dummy.set_var( "recipe_exemplar", rec->ident().str() ); + item_info_cache.dummy = &rec->create_result(); + item_info_cache.dummy->set_var( "recipe_exemplar", rec->ident().str() ); item_info_scroll = 0; item_info_scroll_popup = 0; } std::vector info; - item_info_cache.dummy.info( true, info, count ); - item_info_data data( item_info_cache.dummy.tname( count ), - item_info_cache.dummy.type_name( count ), + item_info_cache.dummy->info( true, info, count ); + item_info_data data( item_info_cache.dummy->tname( count ), + item_info_cache.dummy->type_name( count ), info, {}, scroll_pos ); return data; }; @@ -967,13 +967,14 @@ std::string peek_related_recipe( const recipe *current, const recipe_subset &ava std::sort( related_components.begin(), related_components.end(), compare_second ); // current recipe result std::vector> related_results; - item tmp = current->create_result(); + item &tmp = current->create_result(); itype_id tid; if( tmp.contents.empty() ) { // use this item tid = tmp.typeId(); } else { // use the contained item tid = tmp.contents.front().typeId(); } + tmp.destroy(); const std::set &known_recipes = u.get_learned_recipes().of_component( tid ); for( const auto *b : known_recipes ) { if( available.contains( *b ) ) { diff --git a/src/creature.cpp b/src/creature.cpp index a0af29e95994..dbf797bbf23a 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -791,10 +791,11 @@ void Creature::deal_projectile_attack( Creature *source, dealt_projectile_attack // if its a tameable animal, its a good way to catch them if they are running away, like them ranchers do! // we assume immediate success, then certain monster types immediately break free in monster.cpp move_effects() if( z ) { - const item &drop_item = proj.get_drop(); + item &drop_item = proj.get_drop(); if( !drop_item.is_null() ) { z->add_effect( effect_tied, 1_turns, num_bp ); - z->tied_item = cata::make_value( drop_item ); + //TODO!: check ownering + z->set_tied_item(&drop_item); } else { add_msg( m_debug, "projectile with TANGLE effect, but no drop item specified" ); } diff --git a/src/debug_menu.cpp b/src/debug_menu.cpp index 2f60dac3a2d8..ad4c58e120d8 100644 --- a/src/debug_menu.cpp +++ b/src/debug_menu.cpp @@ -640,23 +640,24 @@ void character_edit_menu( Character &c ) break; } for( auto &it : p.worn ) { - it.on_takeoff( p ); + it->on_takeoff( p ); } p.worn.clear(); p.inv.clear(); - p.weapon = item(); + //TODO!: proper destroy + p.set_weapon(null_item_reference()); break; case edit_character::item_worn: { - item_location loc = game_menus::inv::titled_menu( g->u, _( "Make target equip" ) ); + item* loc = game_menus::inv::titled_menu( g->u, _( "Make target equip" ) ); if( !loc ) { break; } item &to_wear = *loc; if( to_wear.is_armor() ) { p.on_item_wear( to_wear ); - p.worn.push_back( to_wear ); + p.worn.push_back( &to_wear ); } else if( !to_wear.is_null() ) { - p.weapon = to_wear; + p.set_weapon(to_wear); } } break; @@ -1544,7 +1545,7 @@ void debug() break; case DEBUG_SPAWN_CLAIRVOYANCE: - u.i_add( item( "architect_cube", calendar::turn ) ); + u.i_add( *item::spawn( "architect_cube", calendar::turn ) ); break; case DEBUG_MAP_EDITOR: diff --git a/src/dump.cpp b/src/dump.cpp index 23d778b966a3..cefb844be915 100644 --- a/src/dump.cpp +++ b/src/dump.cpp @@ -65,13 +65,6 @@ bool game::dump_stats( const std::string &what, dump_mode mode, test_npcs[ "S6" ] = standard_npc( "S6", { 0, 0, 7 }, { "gloves_hsurvivor", "mask_hsurvivor" }, 4, 8, 10, 8, 10 /* DEX 10, PER 10 */ ); - std::map test_items; - test_items[ "G1" ] = item( "glock_19" ).ammo_set( itype_id( "9mm" ) ); - test_items[ "G2" ] = item( "hk_mp5" ).ammo_set( itype_id( "9mm" ) ); - test_items[ "G3" ] = item( "ar15" ).ammo_set( itype_id( "223" ) ); - test_items[ "G4" ] = item( "remington_700" ).ammo_set( itype_id( "270" ) ); - test_items[ "G4" ].put_in( item( "rifle_scope" ) ); - if( what == "AMMO" ) { header = { "Name", "Ammo", "Volume", "Weight", "Stack", @@ -94,7 +87,7 @@ bool game::dump_stats( const std::string &what, dump_mode mode, }; for( const itype *e : item_controller->all() ) { if( e->ammo ) { - dump( item( e, calendar::turn, item::solitary_tag {} ) ); + dump( *item::spawn_temporary( e, calendar::turn, item::solitary_tag {} ) ); } } @@ -122,7 +115,7 @@ bool game::dump_stats( const std::string &what, dump_mode mode, for( const itype *e : item_controller->all() ) { if( e->armor ) { - item obj( e ); + item &obj = *item::spawn_temporary( e ); if( bp == num_bp || obj.covers( bp ) ) { if( obj.has_flag( flag_VARSIZE ) ) { obj.set_flag( "FIT" ); @@ -156,7 +149,7 @@ bool game::dump_stats( const std::string &what, dump_mode mode, }; for( const itype *e : item_controller->all() ) { - item food( e, calendar::turn, item::solitary_tag {} ); + item &food = *item::spawn_temporary( e, calendar::turn, item::solitary_tag {} ); if( food.is_food() && g->u.can_eat( food ).success() ) { dump( food ); @@ -213,16 +206,16 @@ bool game::dump_stats( const std::string &what, dump_mode mode, }; for( const itype *e : item_controller->all() ) { if( e->gun ) { - item gun( e ); + item &gun = *item::spawn_temporary( e ); if( !gun.magazine_integral() ) { - gun.put_in( item( gun.magazine_default() ) ); + gun.put_in( *item::spawn( gun.magazine_default() ) ); } gun.ammo_set( gun.ammo_default( false ), gun.ammo_capacity() ); dump( test_npcs[ "S1" ], gun ); if( gun.type->gun->barrel_length > 0_ml ) { - gun.put_in( item( "barrel_small" ) ); + gun.put_in( *item::spawn( "barrel_small" ) ); dump( test_npcs[ "S1" ], gun ); } } @@ -310,7 +303,7 @@ bool game::dump_stats( const std::string &what, dump_mode mode, std::vector r; r.push_back( obj.name() ); r.push_back( obj.location ); - int w = std::ceil( to_gram( item( obj.item ).weight() ) / 1000.0 ); + int w = std::ceil( to_gram( item::spawn_temporary( obj.item )->weight() ) / 1000.0 ); r.push_back( std::to_string( w ) ); r.push_back( std::to_string( obj.size / units::legacy_volume_factor ) ); rows.push_back( r ); diff --git a/src/editmap.cpp b/src/editmap.cpp index ce6014c884be..5a465ac2a284 100644 --- a/src/editmap.cpp +++ b/src/editmap.cpp @@ -119,9 +119,7 @@ void edit_json( SAVEOBJ &it ) } if( tmret == 0 ) { try { - SAVEOBJ tmp; - deserialize( tmp, save1 ); - it = std::move( tmp ); + deserialize( it, save1 ); } catch( const std::exception &err ) { popup( "Error on deserialization: %s", err.what() ); } @@ -806,7 +804,7 @@ void editmap::update_view_with_help( const std::string &txt, const std::string & if( !here.has_flag( "CONTAINER", target ) && target_stack_size > 0 ) { trim_and_print( w_info, point( 1, off ), getmaxx( w_info ), c_light_gray, _( "There is a %s there." ), - target_stack.begin()->tname() ); + ( *target_stack.begin() )->tname() ); off++; if( target_stack_size > 1 ) { mvwprintw( w_info, point( 1, off ), vgettext( "There is %d other item there as well.", @@ -1401,8 +1399,8 @@ void editmap::edit_itm() auto items = get_map().i_at( target ); int i = 0; for( auto &an_item : items ) { - ilmenu.addentry( i++, true, 0, "%s%s", an_item.tname(), - an_item.is_emissive() ? " L" : "" ); + ilmenu.addentry( i++, true, 0, "%s%s", an_item->tname(), + an_item->is_emissive() ? " L" : "" ); } ilmenu.addentry( items.size(), true, 'a', _( "Add item" ) ); ilmenu.input_category = "EDIT_ITEMS"; @@ -1428,7 +1426,7 @@ void editmap::edit_itm() ilmenu.query(); if( ilmenu.ret >= 0 && ilmenu.ret < static_cast( items.size() ) ) { - item &it = *items.get_iterator_from_index( ilmenu.ret ); + item &it = **items.get_iterator_from_index( ilmenu.ret ); uilist imenu; imenu.w_x_setup = offsetX; imenu.w_y_setup = [this]( int ) -> int { @@ -1502,8 +1500,8 @@ void editmap::edit_itm() ilmenu.entries.clear(); i = 0; for( auto &an_item : items ) { - ilmenu.addentry( i++, true, 0, "%s%s", an_item.tname(), - an_item.is_emissive() ? " L" : "" ); + ilmenu.addentry( i++, true, 0, "%s%s", an_item->tname(), + an_item->is_emissive() ? " L" : "" ); } ilmenu.addentry( items.size(), true, 'a', pgettext( "item manipulation debug menu entry for adding an item on a tile", "Add item" ) ); diff --git a/src/examine_item_menu.cpp b/src/examine_item_menu.cpp index 6efbb289ece8..02b8343963cf 100644 --- a/src/examine_item_menu.cpp +++ b/src/examine_item_menu.cpp @@ -27,7 +27,7 @@ namespace examine_item_menu { bool run( - item_location loc, + item* loc, const std::function &func_pos_x, const std::function &func_width, menu_pos_t menu_pos @@ -68,7 +68,7 @@ bool run( const bool is_wielded = you.is_wielding( itm ); const bool cant_unwield_if_weapon = is_wielded && !you.can_unwield( itm ).success(); const bool cant_unwield_existing_weapon = you.is_armed() && - !you.can_unwield( you.weapon ).success(); + !you.can_unwield( you.get_weapon() ).success(); const bool cant_takeoff_if_worn = you.is_wearing( itm ) && !you.can_takeoff( itm ).success(); const bool cant_dispose_of = cant_unwield_if_weapon || cant_takeoff_if_worn; @@ -132,12 +132,12 @@ bool run( if( !is_wielded ) { add_entry( "WIELD", rate_wield_item, [&]() { - avatar_action::wield( loc ); + avatar_action::wield( *loc ); return true; } ); } else { add_entry( "UNWIELD", rate_unwield_item, [&]() { - avatar_action::wield( loc ); + avatar_action::wield( *loc ); return true; } ); } @@ -148,7 +148,7 @@ bool run( } ); add_entry( "CHANGE_SIDE", rate_action_change_side( you, itm ), [&]() { - you.change_side( loc ); + you.change_side( *loc ); return true; } ); @@ -158,22 +158,22 @@ bool run( } ); add_entry( "DROP", rate_drop_item, [&]() { - you.drop( loc, you.pos() ); + you.drop( *loc, you.pos() ); return true; } ); add_entry( "UNLOAD", rate_action_unload( you, itm ), [&]() { - you.unload( loc ); + you.unload( *loc ); return true; } ); add_entry( "RELOAD", rate_action_reload( you, itm ), [&]() { - avatar_action::reload( loc ); + avatar_action::reload( *loc ); return true; } ); add_entry( "PART_RELOAD", rate_action_reload( you, itm ), [&]() { - avatar_action::reload( loc, true ); + avatar_action::reload( *loc, true ); return true; } ); @@ -183,7 +183,7 @@ bool run( } ); add_entry( "DISASSEMBLE", rate_action_disassemble( you, itm ), [&]() { - crafting::disassemble( you, loc ); + crafting::disassemble( you, *loc ); return true; } ); diff --git a/src/examine_item_menu.h b/src/examine_item_menu.h index a8d6e7dfe36d..f20fb88845c1 100644 --- a/src/examine_item_menu.h +++ b/src/examine_item_menu.h @@ -18,7 +18,7 @@ enum class menu_pos_t { }; bool run( - item_location loc, + item &loc, const std::function &func_pos_x, const std::function &func_width, menu_pos_t menu_pos diff --git a/src/explosion.cpp b/src/explosion.cpp index 1eff8af483eb..090d4cba6a8c 100644 --- a/src/explosion.cpp +++ b/src/explosion.cpp @@ -1072,7 +1072,7 @@ void emp_blast( const tripoint &p ) if( sight ) { add_msg( _( "The %s beeps erratically and deactivates!" ), critter.name() ); } - here.add_item_or_charges( p, critter.to_item() ); + here.add_item_or_charges( p, *critter.to_item() ); for( auto &ammodef : critter.ammo ) { if( ammodef.second > 0 ) { here.spawn_item( p, ammodef.first, 1, ammodef.second, calendar::turn ); @@ -1120,18 +1120,18 @@ void emp_blast( const tripoint &p ) } // TODO: More effects? //e-handcuffs effects - if( u.weapon.typeId() == itype_e_handcuffs && u.weapon.charges > 0 ) { - u.weapon.unset_flag( "NO_UNWIELD" ); - u.weapon.charges = 0; - u.weapon.active = false; + if( u.get_weapon().typeId() == itype_e_handcuffs && u.get_weapon().charges > 0 ) { + u.get_weapon().unset_flag( "NO_UNWIELD" ); + u.get_weapon().charges = 0; + u.get_weapon().active = false; add_msg( m_good, _( "The %s on your wrists spark briefly, then release your hands!" ), - u.weapon.tname() ); + u.get_weapon().tname() ); } } // Drain any items of their battery charge for( auto &it : here.i_at( p ) ) { - if( it.is_tool() && it.ammo_current() == itype_battery ) { - it.charges = 0; + if( it->is_tool() && it->ammo_current() == itype_battery ) { + it->charges = 0; } } // TODO: Drain NPC energy reserves diff --git a/src/faction.cpp b/src/faction.cpp index 5fa17be7b612..f044a2f4d9ee 100644 --- a/src/faction.cpp +++ b/src/faction.cpp @@ -636,7 +636,7 @@ int npc::faction_display( const catacurses::window &fac_w, const int width ) con mvwprintz( fac_w, point( width, ++y ), fatigue_pair.second, _( "Fatigue: " ) + ( fatigue_pair.first.empty() ? nominal : fatigue_pair.first ) ); int lines = fold_and_print( fac_w, point( width, ++y ), getmaxx( fac_w ) - width - 2, c_white, - _( "Wielding: " ) + weapon.tname() ); + _( "Wielding: " ) + get_weapon().tname() ); y += lines; const auto skillslist = Skill::get_skills_sorted_by( [&]( const Skill & a, const Skill & b ) { diff --git a/src/faction_camp.cpp b/src/faction_camp.cpp index e3f869e39d66..0081c27fe12e 100644 --- a/src/faction_camp.cpp +++ b/src/faction_camp.cpp @@ -2052,8 +2052,8 @@ void basecamp::start_relay_hide_site() tinymap target_bay; target_bay.load( project_to( forest ), false ); std::vector hide_inv; - for( item &i : target_bay.i_at( point( 11, 10 ) ) ) { - hide_inv.push_back( &i ); + for( item * const &i : target_bay.i_at( point( 11, 10 ) ) ) { + hide_inv.push_back( i ); } std::vector gaining_equipment; if( !hide_inv.empty() ) { @@ -2265,8 +2265,8 @@ void basecamp::start_crafting( const std::string &cur_id, const point &cur_dir, making.required_skills ); if( comp != nullptr ) { components.consume_components(); - for( const item &results : making.create_results( batch_size ) ) { - comp->companion_mission_inv.add_item( results ); + for( item *&results : making.create_results( batch_size ) ) { + comp->companion_mission_inv.add_item( *results ); } } return; @@ -2333,16 +2333,16 @@ static std::pair farm_action( const tripoint_abs_omt &omt_t } item *tmp_seed = seed_inv.back(); seed_inv.pop_back(); - std::list used_seed; + ItemList used_seed; if( tmp_seed->count_by_charges() ) { - used_seed.push_back( *tmp_seed ); + used_seed.push_back( tmp_seed ); tmp_seed->charges -= 1; if( tmp_seed->charges > 0 ) { seed_inv.push_back( tmp_seed ); } } - used_seed.front().set_age( 0_turns ); - farm_map.add_item_or_charges( pos, used_seed.front() ); + used_seed.front()->set_age( 0_turns ); + farm_map.add_item_or_charges( pos, *used_seed.front() ); farm_map.set( pos, t_dirt, f_plant_seed ); } } @@ -2351,10 +2351,11 @@ static std::pair farm_action( const tripoint_abs_omt &omt_t if( farm_map.furn( pos ) == f_plant_harvest ) { // Can't use item_stack::only_item() since there might be fertilizer map_stack items = farm_map.i_at( pos ); - const map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.is_seed(); + const map_stack::iterator seed = std::find_if( items.begin(), + items.end(), []( const item * const & it ) { + return it->is_seed(); } ); - if( seed != items.end() && farm_valid_seed( *seed ) ) { + if( seed != items.end() && farm_valid_seed( **seed ) ) { plots_cnt += 1; if( comp ) { int skillLevel = comp->get_skill_level( skill_survival ); @@ -2362,15 +2363,15 @@ static std::pair farm_action( const tripoint_abs_omt &omt_t int plant_cnt = rng( skillLevel / 2, skillLevel ); plant_cnt = std::min( std::max( plant_cnt, 1 ), 9 ); int seed_cnt = std::max( 1, rng( plant_cnt / 4, plant_cnt / 2 ) ); - for( auto &i : iexamine::get_harvest_items( *seed->type, plant_cnt, + for( auto &i : iexamine::get_harvest_items( *( *seed )->type, plant_cnt, seed_cnt, true ) ) { - here.add_item_or_charges( g->u.pos(), i ); + here.add_item_or_charges( g->u.pos(), *i ); } farm_map.i_clear( pos ); farm_map.furn_set( pos, f_null ); farm_map.ter_set( pos, t_dirt ); } else { - plant_names.insert( item::nname( itype_id( seed->type->seed->fruit_id ) ) ); + plant_names.insert( item::nname( itype_id( ( *seed )->type->seed->fruit_id ) ) ); } } } @@ -2471,7 +2472,8 @@ bool basecamp::start_garage_chop( const point &dir, const tripoint_abs_omt &omt_ for( int prt = 0; prt < car->part_count(); prt++ ) { vehicle_stack contents = car->get_items( prt ); for( auto iter = contents.begin(); iter != contents.end(); ) { - comp->companion_mission_inv.add_item( *iter ); + comp->companion_mission_inv.add_item( **iter ); + //TODO!: check iter = contents.erase( iter ); } bool broken = car->part( prt ).is_broken(); @@ -2504,8 +2506,8 @@ bool basecamp::start_garage_chop( const point &dir, const tripoint_abs_omt &omt_ } comp->companion_mission_inv.add_item( car->part( prt ).properties_to_item() ); } else if( !skill_destroy ) { - for( const item &itm : car->part( prt ).pieces_for_broken_part() ) { - comp->companion_mission_inv.add_item( itm ); + for( item *&itm : car->part( prt ).pieces_for_broken_part() ) { + comp->companion_mission_inv.add_item( *itm ); } } } @@ -2550,8 +2552,8 @@ void basecamp::finish_return( npc &comp, const bool fixed_time, const std::strin if( !cancel ) { for( size_t i = 0; i < comp.companion_mission_inv.size(); i++ ) { for( const auto &it : comp.companion_mission_inv.const_stack( i ) ) { - if( !it.count_by_charges() || it.charges > 0 ) { - place_results( it ); + if( !it->count_by_charges() || it->charges > 0 ) { + place_results( *it ); } } } @@ -2984,8 +2986,8 @@ bool basecamp::farm_return( const std::string &task, const tripoint_abs_omt &omt //Give any seeds the NPC didn't use back to you. for( size_t i = 0; i < comp->companion_mission_inv.size(); i++ ) { for( const auto &it : comp->companion_mission_inv.const_stack( i ) ) { - if( it.charges > 0 ) { - g->u.i_add( it ); + if( it->charges > 0 ) { + g->u.i_add( *it ); } } } @@ -3066,8 +3068,8 @@ void basecamp::search_results( int skill, const item_group_id &group_id, int att for( int i = 0; i < attempts; i++ ) { if( skill > rng( 0, difficulty ) ) { auto result = item_group::item_from( group_id, calendar::turn ); - if( !result.is_null() ) { - place_results( result ); + if( result != nullptr ) { + place_results( *result ); } } } @@ -3117,7 +3119,7 @@ void basecamp::hunting_results( int skill, const std::string &task, int attempts for( int i = 0; i < attempts; i++ ) { if( skill > rng( 0, difficulty ) ) { const mtype_id *target = hunting_targets.pick(); - auto result = item::make_corpse( *target, calendar::turn, "" ); + item &result = item::make_corpse( *target, calendar::turn, "" ); if( !result.is_null() ) { place_results( result ); } @@ -3150,9 +3152,9 @@ int om_harvest_furn( npc &comp, const tripoint &omt_tgt, const furn_id &f, int c continue; } if( bring_back ) { - for( const item &itm : item_group::items_from( furn_tgt.bash.drop_group, + for( item *&itm : item_group::items_from( furn_tgt.bash.drop_group, calendar::turn ) ) { - comp.companion_mission_inv.push_back( itm ); + comp.companion_mission_inv.push_back( *itm ); } harvested++; } @@ -3193,9 +3195,9 @@ int om_harvest_ter( npc &comp, const tripoint_abs_omt &omt_tgt, const ter_id &t, continue; } if( bring_back ) { - for( const item &itm : item_group::items_from( ter_tgt.bash.drop_group, + for( item *&itm : item_group::items_from( ter_tgt.bash.drop_group, calendar::turn ) ) { - comp.companion_mission_inv.push_back( itm ); + comp.companion_mission_inv.push_back( *itm ); } harvested++; target_bay.ter_set( p, ter_tgt.bash.ter_set ); @@ -3283,16 +3285,16 @@ mass_volume om_harvest_itm( npc_ptr comp, const tripoint_abs_omt &omt_tgt, int c tripoint mapmin = tripoint( 0, 0, omt_tgt.z() ); tripoint mapmax = tripoint( 2 * SEEX - 1, 2 * SEEY - 1, omt_tgt.z() ); for( const tripoint &p : target_bay.points_in_rectangle( mapmin, mapmax ) ) { - for( const item &i : target_bay.i_at( p ) ) { - total_m += i.weight( true ); - total_v += i.volume( true ); + for( item *&i : target_bay.i_at( p ) ) { + total_m += i->weight( true ); + total_v += i->volume( true ); total_num += 1; if( take && x_in_y( chance, 100 ) ) { if( comp ) { - comp->companion_mission_inv.push_back( i ); + comp->companion_mission_inv.push_back( *i ); } - harvested_m += i.weight( true ); - harvested_v += i.volume( true ); + harvested_m += i->weight( true ); + harvested_v += i->volume( true ); harvested_num += 1; } } @@ -3518,7 +3520,7 @@ int om_carry_weight_to_trips( const std::vector &itms, npc_ptr comp ) } units::mass max_m = comp ? comp->weight_capacity() - comp->weight_carried() : 30_kilogram; //Assume an additional pack will be carried in addition to normal gear - units::volume sack_v = item( itype_id( "makeshift_sling" ) ).get_storage(); + units::volume sack_v = item::spawn_temporary( itype_id( "makeshift_sling" ) )->get_storage(); units::volume max_v = comp ? comp->volume_capacity() - comp->volume_carried() : sack_v; max_v += sack_v; return om_carry_weight_to_trips( total_m, total_v, max_m, max_v ); @@ -3786,7 +3788,7 @@ std::string basecamp::gathering_description( const std::string &bldg ) for( size_t a = 0; a < 6; a++ ) { const auto items = item_group::items_from( itemlist, calendar::turn ); for( auto &it : items ) { - itemnames[it.display_name()]++; + itemnames[it->display_name()]++; } } // Invert the map to get sorting! @@ -3840,8 +3842,9 @@ std::string camp_car_description( vehicle *car ) for( auto &fuel : fuels ) { std::string fuel_entry = string_format( "%d/%d", car->fuel_left( fuel.first ), car->fuel_capacity( fuel.first ) ); - entry += string_format( ">%s:%s\n", item( fuel.first ).tname(), - right_justify( fuel_entry, 33 - utf8_width( item( fuel.first ).tname() ) ) ); + item *fuel_it = item::spawn_temporary( fuel.first ); + entry += string_format( ">%s:%s\n", fuel_it->tname(), + right_justify( fuel_entry, 33 - utf8_width( fuel_it->tname() ) ) ); } for( const vpart_reference &vpr : car->get_all_parts() ) { if( vpr.part().is_battery() ) { @@ -3916,20 +3919,20 @@ bool basecamp::distribute_food() const double slow_rot_coeff = 0.8 + ( has_provides( "pantry" ) ? 0.05 : 0 ); int total = 0; - std::vector keep_me; + std::vector keep_me; for( const tripoint &p_food_stock_abs : z_food ) { const tripoint p_food_stock = here.getlocal( p_food_stock_abs ); map_stack initial_items = here.i_at( p_food_stock ); - for( item &i : initial_items ) { - const bool is_contained_food = i.is_container() && i.get_contained().is_food(); - const item &innermost_item = is_contained_food ? i.get_contained() : i; + for( item * const &i : initial_items ) { + const bool is_contained_food = i->is_container() && i->get_contained().is_food(); + const item &innermost_item = is_contained_food ? i->get_contained() : *i; const bool would_npcs_eat = innermost_item.is_food() && !innermost_item.rotten() && innermost_item.get_comestible_fun() >= -6; if( !would_npcs_eat ) { - keep_me.push_back( std::move( i ) ); + keep_me.push_back( i ); continue; } @@ -3937,7 +3940,7 @@ bool basecamp::distribute_food() constexpr time_duration slow_rot_threshold = 2_days; // Containers handle spoilage logic internally. // so we would get very large time duration for canned or unspoilable foods. - const time_duration rots_in = time_duration::from_turns( i.spoilage_sort_order() ); + const time_duration rots_in = time_duration::from_turns( i->spoilage_sort_order() ); const double rot_multiplier = ( rots_in >= full_calories_threshold ) ? 1.0 : ( ( rots_in >= slow_rot_threshold ) ? slow_rot_coeff : quick_rot_coeff ); @@ -3953,15 +3956,15 @@ bool basecamp::distribute_food() // Leave empty container. // TODO: make NPCs leave empty food containers elsewhere? - if( i.is_container() ) { - i.contents.clear_items(); - i.on_contents_changed(); - keep_me.push_back( std::move( i ) ); + if( i->is_container() ) { + i->contents.clear_items(); + i->on_contents_changed(); + keep_me.push_back( i ); } } here.i_clear( p_food_stock ); - for( item &i : keep_me ) { - here.add_item_or_charges( p_food_stock, std::move( i ), false ); + for( item * const &i : keep_me ) { + here.add_item_or_charges( p_food_stock, *i, false ); } keep_me.clear(); } @@ -3986,7 +3989,7 @@ int camp_morale( int change ) return yours->likes_u; } -void basecamp::place_results( item result ) +void basecamp::place_results( item &result ) { if( by_radio ) { tinymap target_bay; @@ -4026,8 +4029,8 @@ void apply_camp_ownership( const tripoint &camp_pos, int radius ) for( const tripoint &p : here.points_in_rectangle( camp_pos + point( -radius, -radius ), camp_pos + point( radius, radius ) ) ) { auto items = here.i_at( p.xy() ); - for( item &elem : items ) { - elem.set_owner( g->u ); + for( item * const &elem : items ) { + elem->set_owner( g->u ); } } } diff --git a/src/fstream_utils.h b/src/fstream_utils.h index 662f51a05be4..a081b8f21117 100644 --- a/src/fstream_utils.h +++ b/src/fstream_utils.h @@ -221,6 +221,14 @@ inline void deserialize( T &obj, const std::string &data ) obj.deserialize( jsin ); }, data ); } + +template +inline void deserialize( T *&obj, const std::string &data ) +{ + deserialize_wrapper( [&obj]( JsonIn & jsin ) { + obj = T::spawn( jsin ); + }, data ); +} /**@}*/ #endif // CATA_SRC_FSTREAM_UTILS_H diff --git a/src/fungal_effects.cpp b/src/fungal_effects.cpp index 033125dd96a5..da44bf62f4b6 100644 --- a/src/fungal_effects.cpp +++ b/src/fungal_effects.cpp @@ -209,13 +209,15 @@ void fungal_effects::spread_fungus_one_tile( const tripoint &p, const int growth // Replace the (already existing) seed // Can't use item_stack::only_item() since there might be fertilizer map_stack items = m.i_at( p ); - const map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.is_seed(); + const map_stack::iterator seed = std::find_if( items.begin(), + items.end(), []( const item * const & it ) { + return it->is_seed(); } ); - if( seed == items.end() || !seed->is_seed() ) { + if( seed == items.end() || !( *seed )->is_seed() ) { dbg( DL::Warn ) << "No seed item in the PLANT terrain at position " << p; } else { - *seed = item( "fungal_seeds", calendar::turn ); + //TODO!: check + //*seed = item( "fungal_seeds", calendar::turn ); } } } diff --git a/src/game.cpp b/src/game.cpp index 5b35e051b1e0..75d3c4def2da 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1783,11 +1783,12 @@ void game::autopilot_vehicles() } void game::catch_a_monster( monster *fish, const tripoint &pos, player *p, - const time_duration &catch_duration ) // catching function + const time_duration &/*catch_duration*/ ) // catching function { //spawn the corpse, rotten by a part of the duration - m.add_item_or_charges( pos, item::make_corpse( fish->type->id, calendar::turn + rng( 0_turns, - catch_duration ) ) ); + //TODO!:restore next + //m.add_item_or_charges( pos, item::make_corpse( fish->type->id, calendar::turn + rng( 0_turns, + // catch_duration ) ) ); if( u.sees( pos ) ) { u.add_msg_if_player( m_good, _( "You caught a %s." ), fish->type->nname() ); } @@ -2450,7 +2451,7 @@ bool game::try_get_right_click_action( action_id &act, const tripoint &mouse_tar return false; } - if( !u.weapon.is_gun() ) { + if( !u.get_weapon().is_gun() ) { add_msg( m_info, _( "You are not wielding a ranged weapon." ) ); return false; } @@ -2659,7 +2660,7 @@ bool game::load( const save_t &name ) u = avatar(); u.name = name.player_name(); // This should be initialized more globally (in player/Character constructor) - u.weapon = item( "null", calendar::start_of_cataclysm ); + u.set_weapon(null_item_reference()); if( !read_from_file( playerpath + SAVE_EXTENSION, std::bind( &game::unserialize, this, _1 ) ) ) { return false; } @@ -2896,9 +2897,10 @@ spell_events &game::spell_events_subscriber() return *spell_events_ptr; } -bool game::save() +bool game::save( bool quitting ) { try { + reset_save_ids( time( nullptr ), quitting ); if( !save_player_data() || !save_factions_missions_npcs() || !save_artifacts() || @@ -5014,7 +5016,7 @@ bool game::revive_corpse( const tripoint &p, item &it ) critter.no_extra_death_drops = true; critter.add_effect( effect_downed, 5_turns, num_bp ); - for( const item &component : it.components ) { + for( item * const &component : it.components ) { critter.corpse_components.push_back( component ); } @@ -5040,9 +5042,9 @@ void static delete_cyborg_item( map &m, const tripoint &couch_pos, item *cyborg auto dest_veh = &vp->vehicle(); int dest_part = vp->part_index(); - for( item &it : dest_veh->get_items( dest_part ) ) { - if( &it == cyborg ) { - dest_veh->remove_item( dest_part, &it ); + for( item * const &it : dest_veh->get_items( dest_part ) ) { + if( it == cyborg ) { + dest_veh->remove_item( dest_part, it ); } } @@ -5237,10 +5239,10 @@ bool game::forced_door_closing( const tripoint &p, const ter_id &door_type, int } if( bash_dmg == 0 ) { for( auto &elem : m.i_at( p ) ) { - if( elem.made_of( LIQUID ) ) { + if( elem->made_of( LIQUID ) ) { // Liquids are OK, will be destroyed later continue; - } else if( elem.volume() < 250_ml ) { + } else if( elem->volume() < 250_ml ) { // Dito for small items, will be moved away continue; } @@ -5253,13 +5255,14 @@ bool game::forced_door_closing( const tripoint &p, const ter_id &door_type, int if( m.has_flag( "NOITEM", p ) ) { map_stack items = m.i_at( p ); for( map_stack::iterator it = items.begin(); it != items.end(); ) { - if( it->made_of( LIQUID ) ) { + //TODO!:CHECK + if( ( *it )->made_of( LIQUID ) ) { it = items.erase( it ); continue; } - if( it->made_of( material_id( "glass" ) ) && one_in( 2 ) ) { + if( ( *it )->made_of( material_id( "glass" ) ) && one_in( 2 ) ) { if( can_see ) { - add_msg( m_warning, _( "A %s shatters!" ), it->tname() ); + add_msg( m_warning, _( "A %s shatters!" ), ( *it )->tname() ); } else { add_msg( m_warning, _( "Something shatters!" ) ); } @@ -5269,7 +5272,7 @@ bool game::forced_door_closing( const tripoint &p, const ter_id &door_type, int if( cannot_push ) { return false; } - m.add_item_or_charges( kbp, *it ); + m.add_item_or_charges( kbp, **it ); it = items.erase( it ); } } @@ -5490,7 +5493,7 @@ bool game::npc_menu( npc &who ) actor->head_power >= 0 && actor->torso_power >= 0; }; - item_location loc = game_menus::inv::titled_filter_menu( will_accept, u, _( "Use which item?" ) ); + item* loc = game_menus::inv::titled_filter_menu( will_accept, u, _( "Use which item?" ) ); if( !loc ) { add_msg( _( "Never mind" ) ); @@ -5808,8 +5811,7 @@ void game::peek( const tripoint &p ) u.setpos( prev ); if( result.peek_action && *result.peek_action == PA_BLIND_THROW ) { - item_location loc; - avatar_action::plthrow( u, loc, p ); + avatar_action::plthrow( u, nullptr, p ); } m.invalidate_map_cache( p.z ); } @@ -6117,7 +6119,7 @@ void game::print_items_info( const tripoint &lp, const catacurses::window &w_loo } else { std::map item_names; for( auto &item : m.i_at( lp ) ) { - ++item_names[item.tname()]; + ++item_names[item->tname()]; } const int max_width = getmaxx( w_look ) - column - 1; @@ -7083,14 +7085,14 @@ std::vector game::find_nearby_items( int iRadius ) m.sees_some_items( points_p_it, u ) ) { for( auto &elem : m.i_at( points_p_it ) ) { - const std::string name = elem.tname(); + const std::string name = elem->tname(); const tripoint relative_pos = points_p_it - u.pos(); if( std::find( item_order.begin(), item_order.end(), name ) == item_order.end() ) { item_order.push_back( name ); - temp_items[name] = map_item_stack( &elem, relative_pos ); + temp_items[name] = map_item_stack( elem, relative_pos ); } else { - temp_items[name].add_at_pos( &elem, relative_pos ); + temp_items[name].add_at_pos( elem, relative_pos ); } } } @@ -7897,7 +7899,7 @@ game::vmenu_ret game::list_monsters( const std::vector &monster_list } ); ui.mark_resize(); - const int max_gun_range = u.weapon.gun_range( &u ); + const int max_gun_range = u.get_weapon().gun_range( &u ); const tripoint stored_view_offset = u.view_offset; u.view_offset = tripoint_zero; @@ -8209,7 +8211,7 @@ static std::vector> generate_butcher_stack_d result_strings.reserve( its.size() ); for( const map_stack::iterator &it : its ) { - const std::string tname = it->tname(); + const std::string tname = ( *it )->tname(); size_t s = 0; // Search for the index with a string equivalent to tname for( ; s < result_strings.size(); ++s ) { @@ -8241,7 +8243,7 @@ static void add_corpses( uilist &menu, const std::vector &i int hotkey = get_initial_hotkey( menu_index ); for( const map_stack::iterator &it : its ) { - menu.addentry( menu_index++, true, hotkey, it->get_mtype()->nname() ); + menu.addentry( menu_index++, true, hotkey, ( *it )->get_mtype()->nname() ); hotkey = -1; } } @@ -8255,7 +8257,7 @@ static void add_salvagables( uilist &menu, int hotkey = get_initial_hotkey( menu_index ); for( const auto &stack : stacks ) { - const item &it = *stack.first; + const item &it = **stack.first; //~ Name and number of items listed for cutting up const auto &msg = string_format( pgettext( "butchery menu", "Cut up %s (%d)" ), @@ -8275,7 +8277,7 @@ static void add_disassemblables( uilist &menu, int hotkey = get_initial_hotkey( menu_index ); for( const auto &stack : stacks ) { - const item &it = *stack.first; + const item &it = **stack.first; //~ Name, number of items and time to complete disassembling const auto &msg = string_format( pgettext( "butchery menu", "%s (%d)" ), @@ -8307,17 +8309,17 @@ static void butcher_submenu( const std::vector &corpses, in auto cut_time = [&]( enum butcher_type bt ) { int time_to_cut = 0; if( corpse != -1 ) { - time_to_cut = butcher_time_to_cut( inv, *corpses[corpse], bt ); + time_to_cut = butcher_time_to_cut( inv, **corpses[corpse], bt ); } else { for( const map_stack::iterator &it : corpses ) { - time_to_cut += butcher_time_to_cut( inv, *it, bt ); + time_to_cut += butcher_time_to_cut( inv, **it, bt ); } } return to_string_clipped( time_duration::from_turns( time_to_cut / 100 ) ); }; auto info_on_action = [&]( butcher_type type ) { int corpse_index = corpse == -1 ? 0 : corpse; - butchery_setup setup = consider_butchery( *corpses[corpse_index], you, type ); + butchery_setup setup = consider_butchery( **corpses[corpse_index], you, type ); std::string out; for( const std::string &problem : setup.problems ) { out += "\n" + colorize( problem, c_red ); @@ -8330,7 +8332,7 @@ static void butcher_submenu( const std::vector &corpses, in bool has_organs = false; if( corpse != -1 ) { - const mtype *dead_mon = corpses[corpse]->get_mtype(); + const mtype *dead_mon = ( *corpses[corpse] )->get_mtype(); if( dead_mon ) { for( const harvest_entry &entry : dead_mon->harvest.obj() ) { if( entry.type == "skin" ) { @@ -8466,6 +8468,7 @@ void game::butcher() const item *first_item_without_tools = nullptr; // Indices of relevant items + //TODO!:CHECK std::vector corpses; std::vector disassembles; std::vector salvageables; @@ -8474,8 +8477,8 @@ void game::butcher() // TODO: Properly handle different material whitelists // TODO: Improve quality of this section - auto salvage_filter = []( item it ) { - const auto usable = it.get_usable_item( salvage_string ); + auto salvage_filter = [&]( const item & it ) { + auto usable = it.get_usable_item( salvage_string ); return usable != nullptr; }; @@ -8501,16 +8504,16 @@ void game::butcher() // It's not much additional work to just generate a corpse list and // clear it later, but does make the splitting process nicer. for( map_stack::iterator it = items.begin(); it != items.end(); ++it ) { - if( it->is_corpse() ) { + if( ( *it )->is_corpse() ) { corpses.push_back( it ); } else { - if( ( salvage_tool_index != INT_MIN ) && salvage_iuse->valid_to_cut_up( *it ) ) { + if( ( salvage_tool_index != INT_MIN ) && salvage_iuse->valid_to_cut_up( **it ) ) { salvageables.push_back( it ); } - if( crafting::can_disassemble( u, *it, crafting_inv ).success() ) { + if( crafting::can_disassemble( u, **it, crafting_inv ).success() ) { disassembles.push_back( it ); } else if( !first_item_without_tools ) { - first_item_without_tools = &*it; + first_item_without_tools = *it; } } } @@ -8588,7 +8591,7 @@ void game::butcher() int time_to_disassemble = 0; int time_to_disassemble_all = 0; for( const auto &stack : disassembly_stacks ) { - const int time = recipe_dictionary::get_uncraft( stack.first->typeId() ).time; + const int time = recipe_dictionary::get_uncraft( ( *stack.first )->typeId() ).time; time_to_disassemble += time; time_to_disassemble_all += time * stack.second; } @@ -8601,7 +8604,7 @@ void game::butcher() if( salvage_iuse && salvageables.size() > 1 ) { int time_to_salvage = 0; for( const auto &stack : salvage_stacks ) { - time_to_salvage += salvage_iuse->time_to_cut_up( *stack.first ) * stack.second; + time_to_salvage += salvage_iuse->time_to_cut_up( **stack.first ) * stack.second; } kmenu.addentry_col( MULTISALVAGE, true, 'z', _( "Cut up everything" ), @@ -8656,7 +8659,7 @@ void game::butcher() case MULTIBUTCHER: butcher_submenu( corpses ); for( map_stack::iterator &it : corpses ) { - u.activity.targets.emplace_back( map_cursor( u.pos() ), &*it ); + u.activity.targets.emplace_back( *it ); } break; case MULTIDISASSEMBLE_ONE: @@ -8672,13 +8675,13 @@ void game::butcher() break; case BUTCHER_CORPSE: { butcher_submenu( corpses, indexer_index ); - u.activity.targets.emplace_back( map_cursor( u.pos() ), &*corpses[indexer_index] ); + u.activity.targets.emplace_back( *corpses[indexer_index] ); } break; case BUTCHER_DISASSEMBLE: { // Pick index of first item in the disassembly stack - item *const target = &*disassembly_stacks[indexer_index].first; - crafting::disassemble( u, item_location( map_cursor( u.pos() ), target ) ); + item *const target = *disassembly_stacks[indexer_index].first; + crafting::disassemble( u, *target ); } break; case BUTCHER_SALVAGE: { @@ -8686,9 +8689,8 @@ void game::butcher() debugmsg( "null salve_iuse or salvage_tool" ); } else { // Pick index of first item in the salvage stack - item *const target = &*salvage_stacks[indexer_index].first; - item_location item_loc( map_cursor( u.pos() ), target ); - salvage_iuse->cut_up( u, *salvage_tool, item_loc ); + item *const target = *salvage_stacks[indexer_index].first; + salvage_iuse->cut_up( u, *salvage_tool, *target ); } } break; @@ -8782,7 +8784,7 @@ bool game::disable_robot( const tripoint &p ) query_yn( _( "Deactivate the %s?" ), critter.name() ) ) { u.moves -= 100; - m.add_item_or_charges( p, critter.to_item() ); + m.add_item_or_charges( p, *critter.to_item() ); if( !critter.has_flag( MF_INTERIOR_AMMO ) ) { for( auto &ammodef : critter.ammo ) { if( ammodef.second > 0 ) { @@ -9392,21 +9394,21 @@ point game::place_player( const tripoint &dest_loc ) if( pulp_butcher == "butcher" && u.max_quality( quality_id( "BUTCHER" ) ) > INT_MIN ) { std::vector corpses; - for( item &it : m.i_at( u.pos() ) ) { - corpses.push_back( &it ); + for( item * const &it : m.i_at( u.pos() ) ) { + corpses.push_back( it ); } if( !corpses.empty() ) { u.assign_activity( activity_id( "ACT_BUTCHER" ), 0, true ); - for( item *it : corpses ) { - u.activity.targets.emplace_back( map_cursor( u.pos() ), it ); + for( item *&it : corpses ) { + u.activity.targets.emplace_back( it ); } } } else if( pulp_butcher == "pulp" || pulp_butcher == "pulp_adjacent" ) { const auto pulp = [&]( const tripoint & pos ) { for( const auto &maybe_corpse : m.i_at( pos ) ) { - if( maybe_corpse.is_corpse() && maybe_corpse.can_revive() && - !maybe_corpse.get_mtype()->bloodType().obj().has_acid ) { + if( maybe_corpse->is_corpse() && maybe_corpse->can_revive() && + !maybe_corpse->get_mtype()->bloodType().obj().has_acid ) { u.assign_activity( activity_id( "ACT_PULP" ), calendar::INDEFINITELY_LONG, 0 ); u.activity.placement = m.getabs( pos ); u.activity.auto_resume = true; @@ -9461,16 +9463,16 @@ point game::place_player( const tripoint &dest_loc ) } else if( m.has_items( u.pos() ) ) { std::vector names; std::vector counts; - std::vector items; + std::vector items; for( auto &tmpitem : m.i_at( u.pos() ) ) { - std::string next_tname = tmpitem.tname(); - std::string next_dname = tmpitem.display_name(); - bool by_charges = tmpitem.count_by_charges(); + std::string next_tname = tmpitem->tname(); + std::string next_dname = tmpitem->display_name(); + bool by_charges = tmpitem->count_by_charges(); bool got_it = false; for( size_t i = 0; i < names.size(); ++i ) { if( by_charges && next_tname == names[i] ) { - counts[i] += tmpitem.charges; + counts[i] += tmpitem->charges; got_it = true; break; } else if( next_dname == names[i] ) { @@ -9481,10 +9483,10 @@ point game::place_player( const tripoint &dest_loc ) } if( !got_it ) { if( by_charges ) { - names.push_back( tmpitem.tname( tmpitem.charges ) ); - counts.push_back( tmpitem.charges ); + names.push_back( tmpitem->tname( tmpitem->charges ) ); + counts.push_back( tmpitem->charges ); } else { - names.push_back( tmpitem.display_name( 1 ) ); + names.push_back( tmpitem->display_name( 1 ) ); counts.push_back( 1 ); } items.push_back( tmpitem ); @@ -9494,10 +9496,10 @@ point game::place_player( const tripoint &dest_loc ) } } for( size_t i = 0; i < names.size(); ++i ) { - if( !items[i].count_by_charges() ) { - names[i] = items[i].display_name( counts[i] ); + if( !items[i]->count_by_charges() ) { + names[i] = items[i]->display_name( counts[i] ); } else { - names[i] = items[i].tname( counts[i] ); + names[i] = items[i]->tname( counts[i] ); } } int and_the_rest = 0; @@ -9688,8 +9690,8 @@ bool game::grabbed_furn_move( const tripoint &dp ) const int dst_items = m.i_at( fdest ).size(); const bool only_liquid_items = std::all_of( m.i_at( fdest ).begin(), m.i_at( fdest ).end(), - [&]( item & liquid_item ) { - return liquid_item.made_of( LIQUID ); + [&]( item * const & liquid_item ) { + return liquid_item->made_of( LIQUID ); } ); const bool dst_item_ok = !m.has_flag( "NOITEM", fdest ) && @@ -9707,7 +9709,7 @@ bool game::grabbed_furn_move( const tripoint &dp ) // Factor in weight of items contained in the furniture. units::mass furniture_contents_weight = 0_gram; for( auto &contained_item : m.i_at( fpos ) ) { - furniture_contents_weight += contained_item.weight(); + furniture_contents_weight += contained_item->weight(); } str_req += furniture_contents_weight / 4_kilogram; if( !canmove ) { @@ -9778,21 +9780,21 @@ bool game::grabbed_furn_move( const tripoint &dp ) if( dst_items > 0 && only_liquid_items ) { m.i_clear( fdest ); } - + //TODO!: CHECK if( src_items > 0 ) { // Move the stuff inside. if( dst_item_ok && src_item_ok ) { // Assume contents of both cells are legal, so we can just swap contents. - std::list temp; + ItemList temp; std::move( m.i_at( fpos ).begin(), m.i_at( fpos ).end(), std::back_inserter( temp ) ); m.i_clear( fpos ); for( auto item_iter = m.i_at( fdest ).begin(); item_iter != m.i_at( fdest ).end(); ++item_iter ) { - m.i_at( fpos ).insert( *item_iter ); + m.i_at( fpos ).insert( **item_iter ); } m.i_clear( fdest ); for( auto &cur_item : temp ) { - m.i_at( fdest ).insert( cur_item ); + m.i_at( fdest ).insert( *cur_item ); } } else { add_msg( _( "Stuff spills from the %s!" ), furntype.name() ); @@ -9852,7 +9854,7 @@ void game::on_move_effects() { // TODO: Move this to a character method if( !u.is_mounted() ) { - const item muscle( "muscle" ); + const item &muscle = *item::spawn_temporary( "muscle" ); for( const bionic_id &bid : u.get_bionic_fueled_with( muscle ) ) { if( u.has_active_bionic( bid ) ) {// active power gen u.mod_power_level( units::from_kilojoule( muscle.fuel_energy() ) * bid->fuel_efficiency ); @@ -10520,16 +10522,16 @@ void game::vertical_move( int movez, bool force, bool peeking ) void game::start_hauling( const tripoint &pos ) { // Find target items and quantities thereof for the new activity - std::vector target_items; + std::vector target_items; std::vector quantities; map_stack items = m.i_at( pos ); - for( item &it : items ) { + for( item * const &it : items ) { // Liquid cannot be picked up - if( it.made_of( LIQUID ) ) { + if( it->made_of( LIQUID ) ) { continue; } - target_items.emplace_back( map_cursor( pos ), &it ); + target_items.emplace_back( it ); // Quantity of 0 means move all quantities.push_back( 0 ); } @@ -11415,7 +11417,7 @@ void game::quicksave() time_t now = time( nullptr ); //timestamp for start of saving procedure //perform save - save(); + save( false ); //Now reset counters for autosaving, so we don't immediately autosave after a quicksave or autosave. moves_since_last_save = 0; last_save_timestamp = now; @@ -11456,7 +11458,7 @@ void game::autosave() void game::process_artifact( item &it, player &p ) { const bool worn = p.is_worn( it ); - const bool wielded = ( &it == &p.weapon ); + const bool wielded = ( &it == &p.get_weapon() ); std::vector effects = it.type->artifact->effects_carried; if( worn ) { const std::vector &ew = it.type->artifact->effects_worn; @@ -11663,7 +11665,7 @@ bool check_art_charge_req( item &it ) player &p = g->u; bool reqsmet = true; const bool worn = p.is_worn( it ); - const bool wielded = ( &it == &p.weapon ); + const bool wielded = ( &it == &p.get_weapon() ); const bool heldweapon = ( wielded && !it.is_armor() ); //don't charge wielded clothes map &here = get_map(); switch( it.type->artifact->charge_req ) { @@ -11684,7 +11686,7 @@ bool check_art_charge_req( item &it ) if( it.covers( bp ) || ( heldweapon && ( bp == bp_hand_r || bp == bp_hand_l ) ) ) { reqsmet = true; for( auto &i : p.worn ) { - if( i.covers( bp ) && ( &it != &i ) && i.get_coverage() > 50 ) { + if( i->covers( bp ) && ( &it != i ) && i->get_coverage() > 50 ) { reqsmet = false; break; //This one's no good, check the next body part } @@ -11959,7 +11961,7 @@ void game::add_artifact_dreams( ) { //If player is sleeping, get a dream from a carried artifact //Don't need to check that player is sleeping here, that's done before calling - std::list art_items = g->u.get_artifact_items(); + std::vector art_items = g->u.get_artifact_items(); std::vector valid_arts; std::vector> valid_dreams; // Tracking separately so we only need to check its req once diff --git a/src/game.h b/src/game.h index cf131da26261..c52aac342633 100644 --- a/src/game.h +++ b/src/game.h @@ -211,7 +211,7 @@ class game bool dump_stats( const std::string &what, dump_mode mode, const std::vector &opts ); /** Returns false if saving failed. */ - bool save(); + bool save( bool quitting ); /** Returns a list of currently active character saves. */ std::vector list_active_characters(); @@ -609,8 +609,8 @@ class game void draw_trail_to_square( const tripoint &t, bool bDrawX ); /** Custom-filtered menu for inventory and nearby items and those that within specified radius */ - item_location inv_map_splice( item_filter filter, const std::string &title, int radius = 0, - const std::string &none_message = "" ); + item *inv_map_splice( item_filter filter, const std::string &title, int radius = 0, + const std::string &none_message = "" ); bool has_gametype() const; special_game_id gametype() const; diff --git a/src/game_inventory.cpp b/src/game_inventory.cpp index b73f203cb74a..775094d62e6e 100644 --- a/src/game_inventory.cpp +++ b/src/game_inventory.cpp @@ -29,7 +29,6 @@ #include "inventory.h" #include "inventory_ui.h" #include "item.h" -#include "item_location.h" #include "itype.h" #include "iuse.h" #include "iuse_actor.h" @@ -88,17 +87,16 @@ static const std::string flag_USE_EAT_VERB( "USE_EAT_VERB" ); static const flag_str_id flag_BIONIC_NPC_USABLE( "BIONIC_NPC_USABLE" ); using item_filter = std::function; -using item_location_filter = std::function; class inventory_filter_preset : public inventory_selector_preset { public: - inventory_filter_preset( const item_location_filter &filter ); + inventory_filter_preset( const item_filter &filter ); - bool is_shown( const item_location &location ) const override; + bool is_shown( const item* location ) const override; private: - item_location_filter filter; + item_filter filter; }; namespace @@ -136,23 +134,16 @@ int anesthetic_requirement( int mult ) } // namespace -inventory_filter_preset::inventory_filter_preset( const item_location_filter &filter ) +inventory_filter_preset::inventory_filter_preset( const item_filter &filter ) : filter( filter ) {} -bool inventory_filter_preset::is_shown( const item_location &location ) const +bool inventory_filter_preset::is_shown( const item *location ) const { - return filter( location ); + return filter( *location ); } -static item_location_filter convert_filter( const item_filter &filter ) -{ - return [ &filter ]( const item_location & loc ) { - return filter( *loc ); - }; -} - -static item_location inv_internal( player &u, const inventory_selector_preset &preset, +static item* inv_internal( player &u, const inventory_selector_preset &preset, const std::string &title, int radius, const std::string &none_message, const std::string &hint = std::string() ) @@ -206,10 +197,10 @@ static item_location inv_internal( player &u, const inventory_selector_preset &p ? _( "You don't have the necessary item at hand." ) : none_message; popup( msg, PF_GET_KEY ); - return item_location(); + return nullptr; } - item_location location = inv_s.execute(); + item* location = inv_s.execute(); if( inv_s.keep_open ) { inv_s.keep_open = false; @@ -245,9 +236,9 @@ void game_menus::inv::common( avatar &you ) inv_s.clear_items(); inv_s.add_character_items( you ); - const item_location &location = inv_s.execute(); + item *location = inv_s.execute(); - if( location == item_location::nowhere ) { + if( location==nullptr ) { if( inv_s.keep_open ) { inv_s.keep_open = false; continue; @@ -262,19 +253,19 @@ void game_menus::inv::common( avatar &you ) const auto func_width = []() { return 50; }; - started_action = examine_item_menu::run( location, func_pos_x, func_width, + started_action = examine_item_menu::run( *location, func_pos_x, func_width, examine_item_menu::menu_pos_t::right ); } while( !started_action ); } -item_location game_menus::inv::titled_filter_menu( item_filter filter, avatar &you, +item* game_menus::inv::titled_filter_menu( item_filter filter, avatar &you, const std::string &title, const std::string &none_message ) { - return inv_internal( you, inventory_filter_preset( convert_filter( filter ) ), + return inv_internal( you, inventory_filter_preset( filter ), title, -1, none_message ); } -item_location game_menus::inv::titled_menu( avatar &you, const std::string &title, +item* game_menus::inv::titled_menu( avatar &you, const std::string &title, const std::string &none_message ) { const std::string msg = none_message.empty() ? _( "Your inventory is empty." ) : none_message; @@ -286,44 +277,44 @@ class armor_inventory_preset: public inventory_selector_preset public: armor_inventory_preset( player &pl, const std::string &color_in ) : p( pl ), color( color_in ) { - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_number_string( loc->get_encumber( p ) ); }, _( "ENCUMBRANCE" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return loc->get_storage() > 0_ml ? string_format( "<%s>%s", color, format_volume( loc->get_storage() ) ) : std::string(); }, _( "STORAGE" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return string_format( "<%s>%d%%", color, loc->get_coverage() ); }, _( "COVERAGE" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_number_string( loc->get_warmth() ); }, _( "WARMTH" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_number_string( loc->bash_resist() ); }, _( "BASH" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_number_string( loc->cut_resist() ); }, _( "CUT" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_number_string( loc->bullet_resist() ); }, _( "BULLET" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_number_string( loc->acid_resist() ); }, _( "ACID" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_number_string( loc->fire_resist() ); }, _( "FIRE" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_number_string( loc->get_env_resist() ); }, _( "ENV" ) ); } @@ -345,11 +336,11 @@ class wear_inventory_preset: public armor_inventory_preset armor_inventory_preset( p, color ) {} - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { return loc->is_armor() && !p.is_worn( *loc ); } - std::string get_denial( const item_location &loc ) const override { + std::string get_denial( const item*loc ) const override { const auto ret = p.can_wear( *loc ); if( !ret.success() ) { @@ -360,7 +351,7 @@ class wear_inventory_preset: public armor_inventory_preset } }; -item_location game_menus::inv::wear( player &p ) +item* game_menus::inv::wear( player &p ) { return inv_internal( p, wear_inventory_preset( p, "color_yellow" ), _( "Wear item" ), 1, _( "You have nothing to wear." ) ); @@ -373,11 +364,11 @@ class take_off_inventory_preset: public armor_inventory_preset armor_inventory_preset( p, color ) {} - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item *loc ) const override { return loc->is_armor() && p.is_worn( *loc ); } - std::string get_denial( const item_location &loc ) const override { + std::string get_denial( const item* loc ) const override { const ret_val ret = p.can_takeoff( *loc ); if( !ret.success() ) { @@ -388,33 +379,33 @@ class take_off_inventory_preset: public armor_inventory_preset } }; -item_location game_menus::inv::take_off( avatar &you ) +item* game_menus::inv::take_off( avatar &you ) { return inv_internal( you, take_off_inventory_preset( you, "color_red" ), _( "Take off item" ), 1, _( "You don't wear anything." ) ); } -item_location game::inv_map_splice( item_filter filter, const std::string &title, int radius, +item* game::inv_map_splice( item_filter filter, const std::string &title, int radius, const std::string &none_message ) { - return inv_internal( u, inventory_filter_preset( convert_filter( filter ) ), + return inv_internal( u, inventory_filter_preset( filter ), title, radius, none_message ); } -item_location game_menus::inv::container_for( avatar &you, const item &liquid, int radius ) +item* game_menus::inv::container_for( avatar &you, const item &liquid, int radius ) { - const auto filter = [ &liquid ]( const item_location & location ) { - if( location.where() == item_location::type::character ) { + const auto filter = [ &liquid ]( const item& location ) { + if( location.where() == item::item_location_type::character ) { Character *character = g->critter_at( location.position() ); if( character == nullptr ) { debugmsg( "Invalid location supplied to the liquid filter: no character found." ); return false; } - return location->get_remaining_capacity_for_liquid( liquid, *character ) > 0; + return location.get_remaining_capacity_for_liquid( liquid, *character ) > 0; } - const bool allow_buckets = location.where() == item_location::type::map; - return location->get_remaining_capacity_for_liquid( liquid, allow_buckets ) > 0; + const bool allow_buckets = location.where() == item::item_location_type::map; + return location.get_remaining_capacity_for_liquid( liquid, allow_buckets ) > 0; }; return inv_internal( you, inventory_filter_preset( filter ), @@ -428,7 +419,7 @@ class pickup_inventory_preset : public inventory_selector_preset public: pickup_inventory_preset( const player &p ) : p( p ) {} - std::string get_denial( const item_location &loc ) const override { + std::string get_denial( const item*loc ) const override { if( !p.has_item( *loc ) ) { if( loc->made_of( LIQUID ) ) { return _( "Can't pick up spilt liquids" ); @@ -454,12 +445,12 @@ class disassemble_inventory_preset : public pickup_inventory_preset check_components = true; - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { const auto &req = get_recipe( loc ).disassembly_requirements(); if( req.is_empty() ) { return std::string(); } - const item *i = loc.get_item(); + const item *i = loc; const auto components = i->get_uncraft_components(); return enumerate_as_string( components.begin(), components.end(), []( const decltype( components )::value_type & comps ) { @@ -467,16 +458,16 @@ class disassemble_inventory_preset : public pickup_inventory_preset } ); }, _( "YIELD" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return to_string_clipped( time_duration::from_turns( get_recipe( loc ).time / 100 ) ); }, _( "TIME" ) ); } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item *loc ) const override { return get_recipe( loc ); } - std::string get_denial( const item_location &loc ) const override { + std::string get_denial( const item*loc ) const override { const ret_val ret = crafting::can_disassemble( p, *loc, inv ); if( !ret.success() ) { return ret.str(); @@ -485,7 +476,7 @@ class disassemble_inventory_preset : public pickup_inventory_preset } protected: - const recipe &get_recipe( const item_location &loc ) const { + const recipe &get_recipe( const item*loc ) const { return recipe_dictionary::get_uncraft( loc->typeId() ); } @@ -494,7 +485,7 @@ class disassemble_inventory_preset : public pickup_inventory_preset const inventory &inv; }; -item_location game_menus::inv::disassemble( player &p ) +item* game_menus::inv::disassemble( player &p ) { return inv_internal( p, disassemble_inventory_preset( p, p.crafting_inventory() ), _( "Disassemble item" ), 1, @@ -506,16 +497,16 @@ class comestible_inventory_preset : public inventory_selector_preset public: comestible_inventory_preset( const player &p ) : p( p ) { - append_cell( [ &p, this ]( const item_location & loc ) { + append_cell( [ &p, this ]( const item* loc ) { const nutrients nutr = p.compute_effective_nutrients( get_consumable_item( loc ) ); return good_bad_none( nutr.kcal ); }, _( "CALORIES" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item*loc ) { return good_bad_none( get_edible_comestible( loc ).quench ); }, _( "QUENCH" ) ); - append_cell( [ &p, this ]( const item_location & loc ) { + append_cell( [ &p, this ]( const item* loc ) { const item &it = get_consumable_item( loc ); const int consume_fun = p.fun_for( get_consumable_item( loc ) ).first; if( consume_fun < 0 && p.has_active_bionic( bio_taste_blocker ) && @@ -528,7 +519,7 @@ class comestible_inventory_preset : public inventory_selector_preset } }, _( "JOY" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { const time_duration spoils = get_edible_comestible( loc ).spoils; if( spoils > 0_turns ) { return to_string_clipped( spoils ); @@ -537,7 +528,7 @@ class comestible_inventory_preset : public inventory_selector_preset return std::string( _( "indefinite" ) ); }, _( "SHELF LIFE" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { const item &it = get_consumable_item( loc ); int converted_volume_scale = 0; @@ -549,7 +540,7 @@ class comestible_inventory_preset : public inventory_selector_preset return string_format( _( "%.2f%s" ), converted_volume, volume_units_abbr() ); }, _( "VOLUME" ) ); - append_cell( [this]( const item_location & loc ) { + append_cell( [this]( const item* loc ) { if( g->u.can_estimate_rot() ) { const islot_comestible item = get_edible_comestible( loc ); if( item.spoils > 0_turns ) { @@ -560,7 +551,7 @@ class comestible_inventory_preset : public inventory_selector_preset return std::string(); }, _( "FRESHNESS" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { if( g->u.can_estimate_rot() ) { const islot_comestible item = get_edible_comestible( loc ); if( item.spoils > 0_turns ) { @@ -573,7 +564,7 @@ class comestible_inventory_preset : public inventory_selector_preset return std::string(); }, _( "SPOILS IN" ) ); - append_cell( [ this, &p ]( const item_location & loc ) { + append_cell( [ this, &p ]( const item* loc ) { std::string cbm_name; switch( p.get_cbm_rechargeable_with( get_consumable_item( loc ) ) ) { @@ -601,23 +592,23 @@ class comestible_inventory_preset : public inventory_selector_preset return std::string(); }, _( "CBM" ) ); - append_cell( [ this, &p ]( const item_location & loc ) { + append_cell( [ this, &p ]( const item* loc ) { return good_bad_none( p.get_acquirable_energy( get_consumable_item( loc ) ) ); }, _( "ENERGY (kJ)" ) ); } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { // If an item was inserted into a non-container, we can't eat it. // For example, we couldn't eat an item mod made of meat return p.can_consume( *loc ) && - ( loc.where() != item_location::type::container || loc.parent_item()->is_container() ); + ( loc->where() != item::item_location_type::container || loc->parent_item()->is_container() ); } - std::string get_denial( const item_location &loc ) const override { + std::string get_denial( const item*loc ) const override { const item &med = !( *loc ).is_container_empty() && ( *loc ).get_contained().is_medication() && ( *loc ).get_contained().type->has_use() ? ( *loc ).get_contained() : *loc; - if( loc->made_of( LIQUID ) && !g->m.has_flag( flag_LIQUIDCONT, loc.position() ) ) { + if( loc->made_of( LIQUID ) && !g->m.has_flag( flag_LIQUIDCONT, loc->position() ) ) { return _( "Can't drink spilt liquids" ); } @@ -651,7 +642,7 @@ class comestible_inventory_preset : public inventory_selector_preset } protected: - int get_order( const item_location &loc, const time_duration &time ) const { + int get_order( const item*loc, const time_duration &time ) const { if( time > 0_turns && !( loc->type->container && loc->type->container->preserves ) ) { return 0; } else if( get_consumable_item( loc ).rotten() ) { @@ -669,11 +660,11 @@ class comestible_inventory_preset : public inventory_selector_preset // WARNING: this can return consumables which are not necessarily possessing // the comestible type. please dereference responsibly. - const item &get_consumable_item( const item_location &loc ) const { + const item &get_consumable_item( const item* loc ) const { return p.get_consumable_from( const_cast( *loc ) ); } - const islot_comestible &get_edible_comestible( const item_location &loc ) const { + const islot_comestible &get_edible_comestible( const item*loc ) const { return get_edible_comestible( get_consumable_item( loc ) ); } @@ -686,7 +677,7 @@ class comestible_inventory_preset : public inventory_selector_preset return dummy; } - time_duration get_time_left( const item_location &loc ) const { + time_duration get_time_left( const item*loc ) const { time_duration time_left = 0_turns; const time_duration shelf_life = get_edible_comestible( loc ).spoils; if( shelf_life > 0_turns ) { @@ -704,7 +695,7 @@ class comestible_inventory_preset : public inventory_selector_preset return time_left; } - std::string get_time_left_rounded( const item_location &loc ) const { + std::string get_time_left_rounded( const item*loc ) const { const item &it = get_consumable_item( loc ); if( it.is_going_bad() ) { return _( "soon!" ); @@ -714,7 +705,7 @@ class comestible_inventory_preset : public inventory_selector_preset return to_string_approx( time_left ); } - std::string get_freshness( const item_location &loc ) { + std::string get_freshness( const item*loc ) { const item &it = get_consumable_item( loc ); const double rot_progress = it.get_relative_rot(); if( it.is_fresh() ) { @@ -755,7 +746,7 @@ static std::string get_consume_needs_hint( player &p ) return hint; } -item_location game_menus::inv::consume( player &p ) +item* game_menus::inv::consume( player &p ) { if( !g->u.has_activity( ACT_EAT_MENU ) ) { g->u.assign_activity( ACT_EAT_MENU ); @@ -773,7 +764,7 @@ class comestible_filtered_inventory_preset : public comestible_inventory_preset comestible_filtered_inventory_preset( const player &p, bool( *predicate )( const item &it ) ) : comestible_inventory_preset( p ), predicate( predicate ) {} - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { return comestible_inventory_preset::is_shown( loc ) && predicate( get_consumable_item( loc ) ); } @@ -782,7 +773,7 @@ class comestible_filtered_inventory_preset : public comestible_inventory_preset bool( *predicate )( const item &it ); }; -item_location game_menus::inv::consume_food( player &p ) +item* game_menus::inv::consume_food( player &p ) { if( !g->u.has_activity( ACT_CONSUME_FOOD_MENU ) ) { g->u.assign_activity( ACT_CONSUME_FOOD_MENU ); @@ -797,7 +788,7 @@ item_location game_menus::inv::consume_food( player &p ) get_consume_needs_hint( p ) ); } -item_location game_menus::inv::consume_drink( player &p ) +item* game_menus::inv::consume_drink( player &p ) { if( !g->u.has_activity( ACT_CONSUME_DRINK_MENU ) ) { g->u.assign_activity( ACT_CONSUME_DRINK_MENU ); @@ -812,7 +803,7 @@ item_location game_menus::inv::consume_drink( player &p ) get_consume_needs_hint( p ) ); } -item_location game_menus::inv::consume_meds( player &p ) +item* game_menus::inv::consume_meds( player &p ) { if( !g->u.has_activity( ACT_CONSUME_MEDS_MENU ) ) { g->u.assign_activity( ACT_CONSUME_MEDS_MENU ); @@ -831,7 +822,7 @@ class activatable_inventory_preset : public pickup_inventory_preset public: activatable_inventory_preset( const player &p ) : pickup_inventory_preset( p ), p( p ) { if( get_option( "INV_USE_ACTION_NAMES" ) ) { - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { const item &it = !( *loc ).is_container_empty() && ( *loc ).get_contained().is_medication() && ( *loc ).get_contained().type->has_use() ? ( *loc ).get_contained() : *loc; return string_format( "%s", get_action_name( it ) ); @@ -839,7 +830,7 @@ class activatable_inventory_preset : public pickup_inventory_preset } } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { if( !( *loc ).is_container_empty() && ( *loc ).get_contained().is_medication() && ( *loc ).get_contained().type->has_use() ) { return true; @@ -847,7 +838,7 @@ class activatable_inventory_preset : public pickup_inventory_preset return loc->type->has_use(); } - std::string get_denial( const item_location &loc ) const override { + std::string get_denial( const item*loc ) const override { const item &it = !( *loc ).is_container_empty() && ( *loc ).get_contained().is_medication() && ( *loc ).get_contained().type->has_use() ? ( *loc ).get_contained() : *loc; const auto &uses = it.type->use_methods; @@ -894,7 +885,7 @@ class activatable_inventory_preset : public pickup_inventory_preset const player &p; }; -item_location game_menus::inv::use( avatar &you ) +item* game_menus::inv::use( avatar &you ) { return inv_internal( you, activatable_inventory_preset( you ), _( "Use item" ), 1, @@ -905,7 +896,7 @@ class gunmod_inventory_preset : public inventory_selector_preset { public: gunmod_inventory_preset( const player &p, const item &gunmod ) : p( p ), gunmod( gunmod ) { - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { const auto odds = get_odds( loc ); if( odds.first >= 100 ) { @@ -915,25 +906,25 @@ class gunmod_inventory_preset : public inventory_selector_preset return string_format( "%d%%", odds.first ); }, _( "SUCCESS CHANCE" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return good_bad_none( get_odds( loc ).second ); }, _( "DAMAGE RISK" ) ); } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { return loc->is_gun() && !loc->is_gunmod(); } - std::string get_denial( const item_location &loc ) const override { + std::string get_denial( const item*loc ) const override { const auto ret = loc->is_gunmod_compatible( gunmod ); if( !ret.success() ) { return ret.str(); } - if( !p.meets_requirements( gunmod, *loc ) ) { + if( !p.meets_requirements( gunmod, &*loc ) ) { return string_format( _( "requires at least %s" ), - p.enumerate_unmet_requirements( gunmod, *loc ) ); + p.enumerate_unmet_requirements( gunmod, &*loc ) ); } if( get_odds( loc ).first <= 0 ) { @@ -956,7 +947,7 @@ class gunmod_inventory_preset : public inventory_selector_preset protected: /** @return Odds for successful installation (pair.first) and gunmod damage (pair.second) */ - std::pair get_odds( const item_location &gun ) const { + std::pair get_odds( const item*gun ) const { return p.gunmod_installation_odds( *gun, gunmod ); } @@ -965,7 +956,7 @@ class gunmod_inventory_preset : public inventory_selector_preset const item &gunmod; }; -item_location game_menus::inv::gun_to_modify( player &p, const item &gunmod ) +item*game_menus::inv::gun_to_modify( player &p, const item &gunmod ) { return inv_internal( p, gunmod_inventory_preset( p, gunmod ), _( "Select gun to modify" ), -1, @@ -978,7 +969,7 @@ class read_inventory_preset final: public inventory_selector_preset read_inventory_preset( const player &p ) : p( p ) { const std::string unknown = _( "?" ); - append_cell( [ this, &p ]( const item_location & loc ) -> std::string { + append_cell( [ this, &p ]( const item* loc ) -> std::string { if( loc->type->can_use( "MA_MANUAL" ) ) { return _( "martial arts" ); } @@ -1004,17 +995,17 @@ class read_inventory_preset final: public inventory_selector_preset skill.level() ); }, _( "TRAINS (CURRENT)" ), unknown ); - append_cell( [ this ]( const item_location & loc ) -> std::string { + append_cell( [ this ]( const item* loc ) -> std::string { const islot_book &book = get_book( loc ); const int unlearned = book.recipes.size() - get_known_recipes( book ); return unlearned > 0 ? std::to_string( unlearned ) : std::string(); }, _( "RECIPES" ), unknown ); - append_cell( [ &p ]( const item_location & loc ) -> std::string { + append_cell( [ &p ]( const item* loc ) -> std::string { return good_bad_none( character_funcs::get_book_fun_for( p, *loc ) ); }, _( "FUN" ), unknown ); - append_cell( [ this, &p, unknown ]( const item_location & loc ) -> std::string { + append_cell( [ this, &p, unknown ]( const item* loc ) -> std::string { std::vector dummy; // This is terrible and needs to be removed asap when this entire file is refactored @@ -1054,11 +1045,11 @@ class read_inventory_preset final: public inventory_selector_preset }, _( "CHAPTER IN" ), unknown ); } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { return loc->is_book(); } - std::string get_denial( const item_location &loc ) const override { + std::string get_denial( const item*loc ) const override { // This is terrible and needs to be removed asap when this entire file is refactored // to use the new avatar class const avatar *u = p.as_avatar(); @@ -1192,11 +1183,11 @@ class read_inventory_preset final: public inventory_selector_preset } private: - const islot_book &get_book( const item_location &loc ) const { + const islot_book &get_book( const item*loc ) const { return *loc->type->book; } - bool is_known( const item_location &loc ) const { + bool is_known( const item*loc ) const { // This is terrible and needs to be removed asap when this entire file is refactored // to use the new avatar class if( const avatar *u = dynamic_cast( &p ) ) { @@ -1218,7 +1209,7 @@ class read_inventory_preset final: public inventory_selector_preset const player &p; }; -item_location game_menus::inv::read( player &pl ) +item*game_menus::inv::read( player &pl ) { const std::string none_msg = pl.is_player() ? _( "You have nothing to read." ) : string_format( _( "%s has nothing to read." ), pl.disp_name() ); @@ -1231,15 +1222,15 @@ class steal_inventory_preset : public pickup_inventory_preset steal_inventory_preset( const avatar &p, const player &victim ) : pickup_inventory_preset( p ), victim( victim ) {} - bool is_shown( const item_location &loc ) const override { - return !victim.is_worn( *loc ) && &victim.weapon != &( *loc ); + bool is_shown( const item*loc ) const override { + return !victim.is_worn( *loc ) && &victim.get_weapon() != &( *loc ); } private: const player &victim; }; -item_location game_menus::inv::steal( avatar &you, player &victim ) +item*game_menus::inv::steal( avatar &you, player &victim ) { return inv_internal( victim, steal_inventory_preset( you, victim ), string_format( _( "Steal from %s" ), victim.name ), -1, @@ -1250,7 +1241,7 @@ class weapon_inventory_preset: public inventory_selector_preset { public: weapon_inventory_preset( const player &p ) : p( p ) { - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { if( !loc->is_gun() ) { return std::string(); } @@ -1282,26 +1273,26 @@ class weapon_inventory_preset: public inventory_selector_preset } }, pgettext( "Shot as damage", "SHOT" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_damage_string( loc->damage_melee( DT_BASH ) ); }, _( "BASH" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_damage_string( loc->damage_melee( DT_CUT ) ); }, _( "CUT" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_damage_string( loc->damage_melee( DT_STAB ) ); }, _( "STAB" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { if( deals_melee_damage( *loc ) ) { return good_bad_none( loc->type->m_to_hit ); } return std::string(); }, _( "MELEE" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { if( deals_melee_damage( *loc ) ) { return string_format( "%d", this->p.attack_cost( *loc ) ); } @@ -1309,7 +1300,7 @@ class weapon_inventory_preset: public inventory_selector_preset }, _( "MOVES" ) ); } - std::string get_denial( const item_location &loc ) const override { + std::string get_denial( const item*loc ) const override { const auto ret = p.can_wield( *loc ); if( !ret.success() ) { @@ -1332,7 +1323,7 @@ class weapon_inventory_preset: public inventory_selector_preset const player &p; }; -item_location game_menus::inv::wield( avatar &you ) +item*game_menus::inv::wield( avatar &you ) { return inv_internal( you, weapon_inventory_preset( you ), _( "Wield item" ), 1, _( "You have nothing to wield." ) ); @@ -1345,7 +1336,7 @@ class holster_inventory_preset: public weapon_inventory_preset weapon_inventory_preset( p ), actor( actor ) { } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { return actor.can_holster( *loc ); } @@ -1353,7 +1344,7 @@ class holster_inventory_preset: public weapon_inventory_preset const holster_actor &actor; }; -item_location game_menus::inv::holster( player &p, item &holster ) +item*game_menus::inv::holster( player &p, item &holster ) { const std::string holster_name = holster.tname( 1, false ); const auto actor = dynamic_cast @@ -1363,7 +1354,7 @@ item_location game_menus::inv::holster( player &p, item &holster ) const std::string msg = string_format( _( "You can't put anything into your %s." ), holster_name ); popup( msg, PF_GET_KEY ); - return item_location(); + return nullptr; } const std::string title = actor->holster_prompt.empty() @@ -1385,11 +1376,11 @@ class saw_barrel_inventory_preset: public weapon_inventory_preset weapon_inventory_preset( p ), p( p ), tool( tool ), actor( actor ) { } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { return loc->is_gun(); } - std::string get_denial( const item_location &loc ) const override { + std::string get_denial( const item*loc ) const override { const auto ret = actor.can_use_on( p, tool, *loc ); if( !ret.success() ) { @@ -1411,21 +1402,21 @@ class salvage_inventory_preset: public inventory_selector_preset salvage_inventory_preset( const salvage_actor *actor ) : actor( actor ) { - append_cell( [ actor ]( const item_location & loc ) { + append_cell( [ actor ]( const item* loc ) { return to_string_clipped( time_duration::from_turns( actor->time_to_cut_up( - *loc.get_item() ) / 100 ) ); + *loc ) / 100 ) ); }, _( "TIME" ) ); } - bool is_shown( const item_location &loc ) const override { - return actor->valid_to_cut_up( *loc.get_item() ); + bool is_shown( const item*loc ) const override { + return actor->valid_to_cut_up( *loc ); } private: const salvage_actor *actor; }; -item_location game_menus::inv::salvage( player &p, const salvage_actor *actor ) +item*game_menus::inv::salvage( player &p, const salvage_actor *actor ) { return inv_internal( p, salvage_inventory_preset( actor ), _( "Cut up what?" ), 1, @@ -1439,7 +1430,7 @@ class repair_inventory_preset: public inventory_selector_preset actor( actor ), main_tool( main_tool ) { } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { return loc->made_of_any( actor->materials ) && !loc->count_by_charges() && !loc->is_firearm() && &*loc != main_tool; } @@ -1449,7 +1440,7 @@ class repair_inventory_preset: public inventory_selector_preset const item *main_tool; }; -item_location game_menus::inv::repair( player &p, const repair_item_actor *actor, +item*game_menus::inv::repair( player &p, const repair_item_actor *actor, const item *main_tool ) { return inv_internal( p, repair_inventory_preset( actor, main_tool ), @@ -1458,14 +1449,14 @@ item_location game_menus::inv::repair( player &p, const repair_item_actor *actor main_tool->type_name( 1 ) ) ); } -item_location game_menus::inv::saw_barrel( player &p, item &tool ) +item*game_menus::inv::saw_barrel( player &p, item &tool ) { const auto actor = dynamic_cast ( tool.type->get_use( "saw_barrel" )->get_actor_ptr() ); if( !actor ) { debugmsg( "Tried to use a wrong item." ); - return item_location(); + return nullptr; } return inv_internal( p, saw_barrel_inventory_preset( p, tool, *actor ), @@ -1481,8 +1472,8 @@ drop_locations game_menus::inv::multidrop( player &p ) { p.inv.restack( p ); - const inventory_filter_preset preset( [ &p ]( const item_location & location ) { - const item &itm = *location; + const inventory_filter_preset preset( [ &p ]( const item& location ) { + const item &itm = location; if( p.is_wielding( itm ) ) { return p.can_unwield( itm ).success(); } else if( p.is_wearing( itm ) ) { @@ -1521,9 +1512,9 @@ drop_locations game_menus::inv::multidrop( player &p ) iuse_locations game_menus::inv::multiwash( Character &ch, int water, int cleanser, bool do_soft, bool do_hard ) { - const inventory_filter_preset preset( [do_soft, do_hard]( const item_location & location ) { - return location->has_flag( "FILTHY" ) && ( ( do_soft && location->is_soft() ) || - ( do_hard && !location->is_soft() ) ); + const inventory_filter_preset preset( [do_soft, do_hard]( const item& location ) { + return location.has_flag( "FILTHY" ) && ( ( do_soft && location.is_soft() ) || + ( do_hard && !location.is_soft() ) ); } ); auto make_raw_stats = [water, cleanser]( const std::map &items @@ -1722,7 +1713,7 @@ void game_menus::inv::swap_letters( player &p ) } } -static item_location autodoc_internal( player &u, player &patient, +static item*autodoc_internal( player &u, player &patient, const inventory_selector_preset &preset, int radius, bool uninstall = false, bool surgeon = false ) { @@ -1737,7 +1728,7 @@ static item_location autodoc_internal( player &u, player &patient, hint = _( "Patient has Sensory Dulling CBM installed. Anesthesia unneeded." ); } else { const inventory &crafting_inv = u.crafting_inventory(); - std::vector a_filter = crafting_inv.items_with( []( const item & it ) { + std::vector a_filter = crafting_inv.items_with( []( const item & it ) { return it.has_quality( qual_ANESTHESIA ); } ); for( const item *anesthesia_item : a_filter ) { @@ -1749,8 +1740,8 @@ static item_location autodoc_internal( player &u, player &patient, } } - std::vector install_programs = patient.crafting_inventory().items_with( []( - const item & it ) -> bool { return it.has_flag( "BIONIC_INSTALLATION_DATA" ); } ); + std::vector< item *> install_programs = patient.crafting_inventory().items_with( []( + const item & it ) -> bool { return it.has_flag( "BIONIC_INSTALLATION_DATA" ); } ); if( !install_programs.empty() ) { hint += string_format( @@ -1775,10 +1766,10 @@ static item_location autodoc_internal( player &u, player &patient, if( inv_s.empty() ) { popup( _( "You don't have any bionics to install." ), PF_GET_KEY ); - return item_location(); + return nullptr; } - item_location location = inv_s.execute(); + item*location = inv_s.execute(); if( inv_s.keep_open ) { inv_s.keep_open = false; @@ -1796,25 +1787,25 @@ class bionic_install_preset: public inventory_selector_preset public: bionic_install_preset( player &pl, player &patient ) : p( pl ), pa( patient ) { - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_failure_chance( loc ); }, _( "COMPLICATION CHANCE" ) ); - append_cell( [ this ]( const item_location & loc ) { + append_cell( [ this ]( const item* loc ) { return get_operation_duration( loc ); }, _( "OPERATION DURATION" ) ); - append_cell( [this]( const item_location & loc ) { + append_cell( [this]( const item* loc ) { return get_anesth_amount( loc ); }, _( "ANESTHETIC REQUIRED" ) ); } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { return loc->is_bionic(); } - std::string get_denial( const item_location &loc ) const override { - const item *it = loc.get_item(); + std::string get_denial( const item*loc ) const override { + const item *it = loc; const itype *itemtype = it->type; const bionic_id &bid = itemtype->bionic->id; @@ -1840,7 +1831,7 @@ class bionic_install_preset: public inventory_selector_preset return _( "Max power capacity already reached" ); } else if( !p.has_enough_anesth( itemtype, pa ) ) { const int weight = 7; - const int duration = loc.get_item()->type->bionic->difficulty * 2; + const int duration = loc->type->bionic->difficulty * 2; return string_format( _( "%i mL" ), anesthetic_requirement( duration * weight ) ); } @@ -1853,21 +1844,21 @@ class bionic_install_preset: public inventory_selector_preset private: // Returns a formatted string of how long the operation will take. - std::string get_operation_duration( const item_location &loc ) { - const int difficulty = loc.get_item()->type->bionic->difficulty; + std::string get_operation_duration( const item*loc ) { + const int difficulty = loc->type->bionic->difficulty; // 20 minutes per bionic difficulty. return to_string( time_duration::from_minutes( difficulty * 20 ) ); } // Failure chance for bionic install. Combines multiple other functions together. - std::string get_failure_chance( const item_location &loc ) { + std::string get_failure_chance( const item*loc ) { - const int difficulty = loc.get_item()->type->bionic->difficulty; + const int difficulty = loc->type->bionic->difficulty; int chance_of_failure = 100; player &installer = p; - std::vector install_programs = p.crafting_inventory().items_with( [loc]( - const item & it ) -> bool { return it.typeId() == loc.get_item()->type->bionic->installation_data; } ); + std::vector install_programs = p.crafting_inventory().items_with( [loc]( + const item & it ) -> bool { return it.typeId() == loc->type->bionic->installation_data; } ); const bool has_install_program = !install_programs.empty(); @@ -1886,10 +1877,10 @@ class bionic_install_preset: public inventory_selector_preset chance_of_failure ); } - std::string get_anesth_amount( const item_location &loc ) { + std::string get_anesth_amount( const item*loc ) { const int weight = 7; - const int duration = loc.get_item()->type->bionic->difficulty * 2; + const int duration = loc->type->bionic->difficulty * 2; return string_format( _( "%i mL" ), anesthetic_requirement( duration * weight ) ); } }; @@ -1900,25 +1891,25 @@ class bionic_install_surgeon_preset : public inventory_selector_preset public: bionic_install_surgeon_preset( player &pl, player &patient ) : p( pl ), pa( patient ) { - append_cell( [this]( const item_location & loc ) { + append_cell( [this]( const item* loc ) { return get_failure_chance( loc ); }, _( "FAILURE CHANCE" ) ); - append_cell( [this]( const item_location & loc ) { + append_cell( [this]( const item* loc ) { return get_operation_duration( loc ); }, _( "OPERATION DURATION" ) ); - append_cell( [this]( const item_location & loc ) { + append_cell( [this]( const item* loc ) { return get_money_amount( loc ); }, _( "PRICE" ) ); } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { return loc->is_bionic(); } - std::string get_denial( const item_location &loc ) const override { - const item *it = loc.get_item(); + std::string get_denial( const item*loc ) const override { + const item *it = loc; const itype *itemtype = it->type; const bionic_id &bid = itemtype->bionic->id; @@ -1948,16 +1939,16 @@ class bionic_install_surgeon_preset : public inventory_selector_preset private: // Returns a formatted string of how long the operation will take. - std::string get_operation_duration( const item_location &loc ) { - const int difficulty = loc.get_item()->type->bionic->difficulty; + std::string get_operation_duration( const item*loc ) { + const int difficulty = loc->type->bionic->difficulty; // 20 minutes per bionic difficulty. return to_string( time_duration::from_minutes( difficulty * 20 ) ); } // Failure chance for bionic install. Combines multiple other functions together. - std::string get_failure_chance( const item_location &loc ) { + std::string get_failure_chance( const item*loc ) { - const int difficulty = loc.get_item()->type->bionic->difficulty; + const int difficulty = loc->type->bionic->difficulty; int chance_of_failure = 100; player &installer = p; @@ -1976,12 +1967,12 @@ class bionic_install_surgeon_preset : public inventory_selector_preset return string_format( _( "%i%%" ), chance_of_failure ); } - std::string get_money_amount( const item_location &loc ) { - return format_money( loc.get_item()->price( true ) * 2 ); + std::string get_money_amount( const item*loc ) { + return format_money( loc->price( true ) * 2 ); } }; -item_location game_menus::inv::install_bionic( player &p, player &patient, bool surgeon ) +item*game_menus::inv::install_bionic( player &p, player &patient, bool surgeon ) { if( surgeon ) { return autodoc_internal( p, patient, bionic_install_surgeon_preset( p, patient ), 5, false, @@ -1997,29 +1988,29 @@ class bionic_uninstall_preset : public inventory_selector_preset public: bionic_uninstall_preset( player &pl, player &patient ) : p( pl ), pa( patient ) { - append_cell( [this]( const item_location & loc ) { + append_cell( [this]( const item* loc ) { return get_failure_chance( loc ); }, _( "FAILURE CHANCE" ) ); - append_cell( [this]( const item_location & loc ) { + append_cell( [this]( const item* loc ) { return get_operation_duration( loc ); }, _( "OPERATION DURATION" ) ); - append_cell( [this]( const item_location & loc ) { + append_cell( [this]( const item* loc ) { return get_anesth_amount( loc ); }, _( "ANESTHETIC REQUIRED" ) ); } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { return loc->has_flag( flag_IN_CBM ); } - std::string get_denial( const item_location &loc ) const override { - const itype *itemtype = loc.get_item()->type; + std::string get_denial( const item*loc ) const override { + const itype *itemtype = loc->type; if( !p.has_enough_anesth( itemtype, pa ) ) { const int weight = 7; - const int duration = loc.get_item()->type->bionic->difficulty * 2; + const int duration = loc->type->bionic->difficulty * 2; return string_format( _( "%i mL" ), anesthetic_requirement( duration * weight ) ); } @@ -2032,17 +2023,17 @@ class bionic_uninstall_preset : public inventory_selector_preset private: // Returns a formatted string of how long the operation will take. - std::string get_operation_duration( const item_location &loc ) { - const int difficulty = loc.get_item()->type->bionic->difficulty; + std::string get_operation_duration( const item*loc ) { + const int difficulty = loc->type->bionic->difficulty; // 20 minutes per bionic difficulty. return to_string( time_duration::from_minutes( difficulty * 20 ) ); } // Failure chance for bionic uninstall. Combines multiple other functions together. - std::string get_failure_chance( const item_location &loc ) { + std::string get_failure_chance( const item*loc ) { // Uninstall difficulty gets a +2 - const int difficulty = loc.get_item()->type->bionic->difficulty + 2; + const int difficulty = loc->type->bionic->difficulty + 2; int chance_of_failure = 100; player &installer = p; @@ -2060,14 +2051,14 @@ class bionic_uninstall_preset : public inventory_selector_preset return string_format( _( "%i%%" ), chance_of_failure ); } - std::string get_anesth_amount( const item_location &loc ) { + std::string get_anesth_amount( const item*loc ) { const int weight = 7; - const int duration = loc.get_item()->type->bionic->difficulty * 2; + const int duration = loc->type->bionic->difficulty * 2; return string_format( _( "%i mL" ), anesthetic_requirement( duration * weight ) ); } }; -item_location game_menus::inv::uninstall_bionic( player &p, player &patient ) +item*game_menus::inv::uninstall_bionic( player &p, player &patient ) { return autodoc_internal( p, patient, bionic_uninstall_preset( p, patient ), 0, true ); } @@ -2080,12 +2071,12 @@ class bionic_sterilize_preset : public inventory_selector_preset p( pl ) { } - bool is_shown( const item_location &loc ) const override { + bool is_shown( const item*loc ) const override { return loc->has_fault( fault_bionic_nonsterile ) && loc->is_bionic(); } - std::string get_denial( const item_location &loc ) const override { - if( loc.get_item()->has_flag( flag_FILTHY ) ) { + std::string get_denial( const item*loc ) const override { + if( loc->has_flag( flag_FILTHY ) ) { return _( "CBM is filthy. Wash it first." ); } @@ -2096,7 +2087,7 @@ class bionic_sterilize_preset : public inventory_selector_preset player &p; }; -static item_location autoclave_internal( player &u, +static item*autoclave_internal( player &u, const inventory_selector_preset &preset, int radius ) { @@ -2114,10 +2105,10 @@ static item_location autoclave_internal( player &u, if( inv_s.empty() ) { popup( _( "You don't have any CBM to sterilize." ), PF_GET_KEY ); - return item_location(); + return nullptr; } - item_location location = inv_s.execute(); + item*location = inv_s.execute(); if( inv_s.keep_open ) { inv_s.keep_open = false; @@ -2129,7 +2120,7 @@ static item_location autoclave_internal( player &u, } while( true ); } -item_location game_menus::inv::sterilize_cbm( player &p ) +item*game_menus::inv::sterilize_cbm( player &p ) { return autoclave_internal( p, bionic_sterilize_preset( p ), 6 ); } diff --git a/src/game_inventory.h b/src/game_inventory.h index 93c47c0dc2c1..5c082668b1dd 100644 --- a/src/game_inventory.h +++ b/src/game_inventory.h @@ -8,7 +8,6 @@ #include #include "item_handling_util.h" -#include "item_location.h" struct tripoint; @@ -22,6 +21,7 @@ class item; class player; class repair_item_actor; class salvage_actor; +class Character; using item_filter = std::function; @@ -31,11 +31,11 @@ namespace game_menus namespace inv { // item selector for all items in @you's inventory. -item_location titled_menu( avatar &you, const std::string &title, - const std::string &none_message = "" ); +item *titled_menu( avatar &you, const std::string &title, + const std::string &none_message = "" ); // item selector for items in @you's inventory with a filter -item_location titled_filter_menu( item_filter filter, avatar &you, - const std::string &title, const std::string &none_message = "" ); +item *titled_filter_menu( item_filter filter, avatar &you, + const std::string &title, const std::string &none_message = "" ); /** * @name Customized inventory menus @@ -71,45 +71,45 @@ drop_locations multidrop( player &p ); iuse_locations multiwash( Character &ch, int water, int cleanser, bool do_soft, bool do_hard ); /** Consuming an item. */ -item_location consume( player &p ); +item *consume( player &p ); /** Consuming a food item via a custom menu. */ -item_location consume_food( player &p ); +item *consume_food( player &p ); /** Consuming a drink item via a custom menu. */ -item_location consume_drink( player &p ); +item *consume_drink( player &p ); /** Consuming a medication item via a custom menu. */ -item_location consume_meds( player &p ); +item *consume_meds( player &p ); /** Choosing a container for liquid. */ -item_location container_for( avatar &you, const item &liquid, int radius = 0 ); +item *container_for( avatar &you, const item &liquid, int radius = 0 ); /** Item disassembling menu. */ -item_location disassemble( player &p ); +item *disassemble( player &p ); /** Gunmod installation menu. */ -item_location gun_to_modify( player &p, const item &gunmod ); +item *gun_to_modify( player &p, const item &gunmod ); /** Book reading menu. */ -item_location read( player &pl ); +item *read( player &pl ); /** Menu for stealing stuff. */ -item_location steal( avatar &you, player &victim ); +item *steal( avatar &you, player &victim ); /** Item activation menu. */ -item_location use( avatar &you ); +item *use( avatar &you ); /** Item wielding/unwielding menu. */ -item_location wield( avatar &you ); +item *wield( avatar &you ); /** Item wielding/unwielding menu. */ -item_location holster( player &p, item &holster ); +item *holster( player &p, item &holster ); /** Choosing a gun to saw down it's barrel. */ -item_location saw_barrel( player &p, item &tool ); +item *saw_barrel( player &p, item &tool ); /** Choose item to wear. */ -item_location wear( player &p ); +item *wear( player &p ); /** Choose item to take off. */ -item_location take_off( avatar &you ); +item *take_off( avatar &you ); /** Item cut up menu. */ -item_location salvage( player &p, const salvage_actor *actor ); +item *salvage( player &p, const salvage_actor *actor ); /** Repair menu. */ -item_location repair( player &p, const repair_item_actor *actor, const item *main_tool ); +item *repair( player &p, const repair_item_actor *actor, const item *main_tool ); /** Bionic install menu. */ -item_location install_bionic( player &p, player &patient, bool surgeon = false ); +item *install_bionic( player &p, player &patient, bool surgeon = false ); /** Bionic uninstall menu. */ -item_location uninstall_bionic( player &p, player &patient ); +item *uninstall_bionic( player &p, player &patient ); /**Autoclave sterilize menu*/ -item_location sterilize_cbm( player &p ); +item *sterilize_cbm( player &p ); /*@}*/ } // namespace inv diff --git a/src/gamemode_defense.cpp b/src/gamemode_defense.cpp index eab605376d0f..5aafcd00220e 100644 --- a/src/gamemode_defense.cpp +++ b/src/gamemode_defense.cpp @@ -976,7 +976,7 @@ void defense_game::caravan() if( current_window == 1 && !items[category_selected].empty() ) { item_count[category_selected][item_selected]++; itype_id tmp_itm = items[category_selected][item_selected]; - int item_price = item( tmp_itm, calendar::start_of_cataclysm ).price( false ); + int item_price = item::spawn_temporary( tmp_itm, calendar::start_of_cataclysm )->price( false ); total_price += caravan_price( g->u, item_price ); if( category_selected == CARAVAN_CART ) { // Find the item in its category for( int i = 1; i < NUM_CARAVAN_CATEGORIES; i++ ) { @@ -1005,7 +1005,7 @@ void defense_game::caravan() item_count[category_selected][item_selected] > 0 ) { item_count[category_selected][item_selected]--; itype_id tmp_itm = items[category_selected][item_selected]; - int item_price = item( tmp_itm, calendar::start_of_cataclysm ).price( false ); + int item_price = item::spawn_temporary( tmp_itm, calendar::start_of_cataclysm )->price( false ); total_price -= caravan_price( g->u, item_price ); if( category_selected == CARAVAN_CART ) { // Find the item in its category for( int i = 1; i < NUM_CARAVAN_CATEGORIES; i++ ) { @@ -1056,21 +1056,21 @@ void defense_game::caravan() g->u.cash -= total_price; bool dropped_some = false; for( size_t i = 0; i < items[0].size(); i++ ) { - item tmp( items[0][i] ); - tmp = tmp.in_its_container(); + for( int j = 0; j < item_count[0][i]; j++ ) { + item *tmp = item::spawn( items[0][i] ); + tmp = &tmp->in_its_container(); - // Guns bought from the caravan should always come with an empty - // magazine. - if( tmp.is_gun() && !tmp.magazine_integral() ) { - tmp.put_in( item( tmp.magazine_default() ) ); - } + // Guns bought from the caravan should always come with an empty + // magazine. + if( tmp->is_gun() && !tmp->magazine_integral() ) { + tmp->put_in( *item::spawn( tmp->magazine_default() ) ); + } - for( int j = 0; j < item_count[0][i]; j++ ) { - if( g->u.can_pick_volume( tmp ) && g->u.can_pick_weight( tmp ) ) { - g->u.i_add( tmp ); + if( g->u.can_pick_volume( *tmp ) && g->u.can_pick_weight( *tmp ) ) { + g->u.i_add( *tmp ); } else { // Could fit it in the inventory! dropped_some = true; - get_map().add_item_or_charges( g->u.pos(), tmp ); + get_map().add_item_or_charges( g->u.pos(), *tmp ); } } } @@ -1149,11 +1149,11 @@ std::vector caravan_items( caravan_category cat ) item_group::ItemList item_list = item_group::items_from( item_group_id( group_id ) ); for( auto &it : item_list ) { - itype_id item_type = it.typeId(); + itype_id item_type = it->typeId(); ret.emplace_back( item_type ); // Add the default magazine types for each gun. - if( it.is_gun() && !it.magazine_integral() ) { - ret.emplace_back( it.magazine_default() ); + if( it->is_gun() && !it->magazine_integral() ) { + ret.emplace_back( it->magazine_default() ); } } return ret; @@ -1246,7 +1246,7 @@ void draw_caravan_items( const catacurses::window &w, std::vector *ite } // THEN print it--if item_selected is valid if( item_selected < static_cast( items->size() ) ) { - item tmp( ( *items )[item_selected], calendar::start_of_cataclysm ); + item &tmp = *item::spawn_temporary( ( *items )[item_selected], calendar::start_of_cataclysm ); fold_and_print( w, point( 1, 12 ), 38, c_white, tmp.info() ); } // Next, clear the item list on the right @@ -1260,7 +1260,8 @@ void draw_caravan_items( const catacurses::window &w, std::vector *ite item::nname( ( *items )[i], ( *counts )[i] ) ); wprintz( w, c_white, " x %2d", ( *counts )[i] ); if( ( *counts )[i] > 0 ) { - int item_price = item( ( *items )[i], calendar::start_of_cataclysm ).price( false ); + int item_price = item::spawn_temporary( ( *items )[i], + calendar::start_of_cataclysm )->price( false ); int price = caravan_price( g->u, item_price * ( *counts )[i] ); wprintz( w, ( price > g->u.cash ? c_red : c_green ), " (%s)", format_money( price ) ); } diff --git a/src/gamemode_tutorial.cpp b/src/gamemode_tutorial.cpp index 03e9cc7053b0..11d1bf107d98 100644 --- a/src/gamemode_tutorial.cpp +++ b/src/gamemode_tutorial.cpp @@ -145,7 +145,7 @@ bool tutorial_game::init() starting_om.clear_mon_groups(); g->u.toggle_trait( trait_QUICK ); - item lighter( "lighter", calendar::start_of_cataclysm ); + item &lighter = *item::spawn( "lighter", calendar::start_of_cataclysm ); lighter.invlet = 'e'; player_character.inv.add_item( lighter, true, false ); player_character.set_skill_level( skill_gun, 5 ); @@ -184,8 +184,8 @@ void tutorial_game::per_turn() map &here = get_map(); if( !tutorials_seen[tut_lesson::LESSON_BUTCHER] ) { - for( const item &it : here.i_at( point( g->u.posx(), g->u.posy() ) ) ) { - if( it.is_corpse() ) { + for( const item * const &it : here.i_at( point( g->u.posx(), g->u.posy() ) ) ) { + if( it->is_corpse() ) { add_message( tut_lesson::LESSON_BUTCHER ); break; } @@ -237,7 +237,7 @@ void tutorial_game::post_action( action_id act ) { switch( act ) { case ACTION_RELOAD_WEAPON: - if( g->u.weapon.is_gun() && !tutorials_seen[tut_lesson::LESSON_GUN_FIRE] ) { + if( g->u.get_weapon().is_gun() && !tutorials_seen[tut_lesson::LESSON_GUN_FIRE] ) { g->place_critter_at( mon_zombie, tripoint( g->u.posx(), g->u.posy() - 6, g->u.posz() ) ); g->place_critter_at( mon_zombie, tripoint( g->u.posx() + 2, g->u.posy() - 5, g->u.posz() ) ); g->place_critter_at( mon_zombie, tripoint( g->u.posx() - 2, g->u.posy() - 5, g->u.posz() ) ); @@ -277,7 +277,7 @@ void tutorial_game::post_action( action_id act ) break; case ACTION_WEAR: { - item it( g->u.last_item, calendar::start_of_cataclysm ); + item &it = *item::spawn_temporary( g->u.last_item, calendar::start_of_cataclysm ); if( it.is_armor() ) { if( it.get_coverage() >= 2 || it.get_thickness() >= 2 ) { add_message( tut_lesson::LESSON_WORE_ARMOR ); @@ -293,7 +293,7 @@ void tutorial_game::post_action( action_id act ) break; case ACTION_WIELD: - if( g->u.weapon.is_gun() ) { + if( g->u.get_weapon().is_gun() ) { add_message( tut_lesson::LESSON_GUN_LOAD ); } break; @@ -302,7 +302,7 @@ void tutorial_game::post_action( action_id act ) add_message( tut_lesson::LESSON_INTERACT ); /* fallthrough */ case ACTION_PICKUP: { - item it( g->u.last_item, calendar::start_of_cataclysm ); + item &it = *item::spawn_temporary( g->u.last_item, calendar::start_of_cataclysm ); if( it.is_armor() ) { add_message( tut_lesson::LESSON_GOT_ARMOR ); } else if( it.is_gun() ) { diff --git a/src/gates.cpp b/src/gates.cpp index f314c1416ea9..7e8420af5807 100644 --- a/src/gates.cpp +++ b/src/gates.cpp @@ -315,12 +315,12 @@ void doors::close_door( map &m, Character &who, const tripoint &closep ) const units::volume max_nudge = 25_liter; const auto toobig = std::find_if( items_in_way.begin(), items_in_way.end(), - [&max_nudge]( const item & it ) { - return it.volume() > max_nudge; + [&max_nudge]( const item * const & it ) { + return it->volume() > max_nudge; } ); if( toobig != items_in_way.end() ) { who.add_msg_if_player( m_info, _( "The %s is too big to just nudge out of the way." ), - toobig->tname() ); + ( *toobig )->tname() ); } else if( items_in_way.stored_volume() > max_nudge ) { who.add_msg_if_player( m_info, _( "There is too much stuff in the way." ) ); } else { @@ -334,7 +334,7 @@ void doors::close_door( map &m, Character &who, const tripoint &closep ) // Just plopping items back on their origin square will displace them to adjacent squares // since the door is closed now. for( auto &elem : items_in_way ) { - m.add_item_or_charges( closep, elem ); + m.add_item_or_charges( closep, *elem ); } m.i_clear( closep ); } diff --git a/src/handle_action.cpp b/src/handle_action.cpp index 57c5e9ce8f8a..23b632d77751 100644 --- a/src/handle_action.cpp +++ b/src/handle_action.cpp @@ -682,7 +682,7 @@ static void smash() } } } - const int move_cost = !u.is_armed() ? 80 : u.weapon.attack_cost() * 0.8; + const int move_cost = !u.is_armed() ? 80 : u.get_weapon().attack_cost() * 0.8; bool didit = false; bool mech_smash = false; int smashskill; @@ -693,7 +693,8 @@ static void smash() mon->type->melee_sides; mech_smash = true; } else { - smashskill = u.str_cur + u.weapon.damage_melee( DT_BASH ); + //TODO!: nullptr check + smashskill = u.str_cur + u.get_weapon().damage_melee( DT_BASH ); } const bool allow_floor_bash = here.has_zlevels(); @@ -743,9 +744,9 @@ static void smash() } bool should_pulp = false; - for( const item &it : here.i_at( smashp ) ) { - if( it.is_corpse() && it.damage() < it.max_damage() && it.can_revive() ) { - if( it.get_mtype()->bloodType()->has_acid ) { + for( const item * const &it : here.i_at( smashp ) ) { + if( it->is_corpse() && it->damage() < it->max_damage() && it->can_revive() ) { + if( it->get_mtype()->bloodType()->has_acid ) { if( query_yn( _( "Are you sure you want to pulp an acid filled corpse?" ) ) ) { should_pulp = true; break; // Don't prompt for the same thing multiple times @@ -773,18 +774,19 @@ static void smash() didit = here.bash( smashp, smashskill, false, false, smash_floor ).did_bash; if( didit ) { if( !mech_smash ) { - u.handle_melee_wear( u.weapon ); - const int mod_sta = ( ( u.weapon.weight() / 10_gram ) + 200 + static_cast + item& weapon=u.get_weapon(); + u.handle_melee_wear( weapon ); + const int mod_sta = ( ( weapon.weight() / 10_gram ) + 200 + static_cast ( get_option( "PLAYER_BASE_STAMINA_REGEN_RATE" ) ) ) * -1; u.mod_stamina( mod_sta ); if( u.get_skill_level( skill_melee ) == 0 ) { u.practice( skill_melee, rng( 0, 1 ) * rng( 0, 1 ) ); } - const int vol = u.weapon.volume() / units::legacy_volume_factor; - if( u.weapon.made_of( material_id( "glass" ) ) && + const int vol = weapon.volume() / units::legacy_volume_factor; + if( weapon.made_of( material_id( "glass" ) ) && rng( 0, vol + 3 ) < vol ) { - add_msg( m_bad, _( "Your %s shatters!" ), u.weapon.tname() ); - u.weapon.spill_contents( u.pos() ); + add_msg( m_bad, _( "Your %s shatters!" ), weapon.tname() ); + weapon.spill_contents( u.pos() ); sounds::sound( u.pos(), 24, sounds::sound_t::combat, "CRACK!", true, "smash", "glass" ); u.deal_damage( nullptr, bodypart_id( "hand_r" ), damage_instance( DT_CUT, rng( 0, vol ) ) ); if( vol > 20 ) { @@ -793,6 +795,7 @@ static void smash() static_cast( vol * .5 ) ) ) ); } u.remove_weapon(); + weapon.destroy(); u.check_dead_state(); } } @@ -1265,10 +1268,13 @@ static void loot() static void wear() { avatar &u = g->u; - item_location loc = game_menus::inv::wear( u ); + item* loc = game_menus::inv::wear( u ); if( loc ) { - u.wear( *loc.obtain( u ) ); + //TODO!: bit weird but I guess this is ok + loc->obtain( u ); + loc->detach(); + u.wear( *loc ); } else { add_msg( _( "Never mind." ) ); } @@ -1277,10 +1283,12 @@ static void wear() static void takeoff() { avatar &u = g->u; - item_location loc = game_menus::inv::take_off( u ); + item* loc = game_menus::inv::take_off( u ); if( loc ) { - u.takeoff( *loc.obtain( u ) ); + loc->obtain( u ); + loc->detach(); + u.takeoff( *loc ); } else { add_msg( _( "Never mind." ) ); } @@ -1290,11 +1298,11 @@ static void read() { avatar &u = g->u; // Can read items from inventory or within one tile (including in vehicles) - item_location loc = game_menus::inv::read( u ); + item* loc = game_menus::inv::read( u ); if( loc ) { if( loc->type->can_use( "learn_spell" ) ) { - item spell_book = *loc.get_item(); + item &spell_book = *loc; spell_book.get_use( "learn_spell" )->call( u, spell_book, spell_book.active, u.pos() ); } else { u.read( loc ); @@ -1309,7 +1317,7 @@ static void reach_attack( avatar &you ) { g->temp_exit_fullscreen(); - target_handler::trajectory traj = target_handler::mode_reach( you, you.weapon ); + target_handler::trajectory traj = target_handler::mode_reach( you, you.get_weapon() ); if( !traj.empty() ) { you.reach_attack( traj.back() ); @@ -1343,21 +1351,21 @@ static void fire() std::vector> actions; for( auto &w : u.worn ) { - if( w.type->can_use( "holster" ) && !w.has_flag( flag_NO_QUICKDRAW ) && - !w.contents.empty() && w.contents.front().is_gun() ) { + if( w->type->can_use( "holster" ) && !w->has_flag( flag_NO_QUICKDRAW ) && + !w->contents.empty() && w->contents.front().is_gun() ) { //~ draw (first) gun contained in holster //~ %1$s: weapon name, %2$s: container name, %3$d: remaining ammo count options.push_back( string_format( pgettext( "holster", "%1$s from %2$s (%3$d)" ), - w.contents.front().tname(), - w.type_name(), - w.contents.front().ammo_remaining() ) ); + w->contents.front().tname(), + w->type_name(), + w->contents.front().ammo_remaining() ) ); - actions.emplace_back( [&] { u.invoke_item( &w, "holster" ); } ); + actions.emplace_back( [&] { u.invoke_item( w, "holster" ); } ); - } else if( w.is_gun() && w.gunmod_find( itype_shoulder_strap ) ) { + } else if( w->is_gun() && w->gunmod_find( itype_shoulder_strap ) ) { // wield item currently worn using shoulder strap - options.push_back( w.display_name() ); - actions.emplace_back( [&] { u.wield( w ); } ); + options.push_back( w->display_name() ); + actions.emplace_back( [&] { u.wield( *w ); } ); } } if( !options.empty() ) { @@ -1368,9 +1376,9 @@ static void fire() } } - if( u.weapon.is_gun() && !u.weapon.gun_current_mode().melee() ) { + if( u.get_weapon().is_gun() && !u.get_weapon().gun_current_mode().melee() ) { avatar_action::fire_wielded_weapon( u ); - } else if( u.weapon.reach_range( u ) > 1 ) { + } else if( u.get_weapon().reach_range( u ) > 1 ) { if( u.has_effect( effect_relax_gas ) ) { if( one_in( 8 ) ) { add_msg( m_good, _( "Your willpower asserts itself, and so do you!" ) ); @@ -1441,7 +1449,7 @@ static void cast_spell() spell &sp = *u.magic->get_spells()[spell_index]; if( u.is_armed() && !sp.has_flag( spell_flag::NO_HANDS ) && - !u.weapon.has_flag( flag_MAGIC_FOCUS ) ) { + !u.get_weapon().has_flag( flag_MAGIC_FOCUS ) ) { add_msg( game_message_params{ m_bad, gmf_bypass_cooldown }, _( "You need your hands free to cast this spell!" ) ); return; @@ -2044,12 +2052,11 @@ bool game::handle_action() break; case ACTION_MEND: - avatar_action::mend( g->u, item_location() ); + avatar_action::mend( g->u, nullptr ); break; case ACTION_THROW: { - item_location loc; - avatar_action::plthrow( g->u, loc ); + avatar_action::plthrow( g->u, nullptr ); break; } @@ -2062,25 +2069,25 @@ bool game::handle_action() break; case ACTION_FIRE_BURST: { - if( u.weapon.gun_set_mode( gun_mode_id( "AUTO" ) ) ) { + if( u.get_weapon().gun_set_mode( gun_mode_id( "AUTO" ) ) ) { avatar_action::fire_wielded_weapon( u ); } break; } case ACTION_SELECT_FIRE_MODE: - if( u.is_armed() && u.weapon.is_gun() && !u.weapon.is_gunmod() ) { - if( u.weapon.gun_all_modes().size() > 1 ) { - u.weapon.gun_cycle_mode(); + if( u.is_armed() && u.get_weapon().is_gun() && !u.get_weapon().is_gunmod() ) { + if( u.get_weapon().gun_all_modes().size() > 1 ) { + u.get_weapon().gun_cycle_mode(); } else { - add_msg( m_info, _( "Your %s has only one firing mode." ), u.weapon.display_name() ); + add_msg( m_info, _( "Your %s has only one firing mode." ), u.get_weapon().display_name() ); } } break; case ACTION_SELECT_DEFAULT_AMMO: - if( u.is_armed() && u.weapon.is_gun() && !u.weapon.is_gunmod() ) { - ranged::prompt_select_default_ammo_for( u, u.weapon ); + if( u.is_armed() && u.get_weapon().is_gun() && !u.get_weapon().is_gunmod() ) { + ranged::prompt_select_default_ammo_for( u, u.get_weapon() ); } break; @@ -2256,7 +2263,7 @@ bool game::handle_action() case ACTION_SAVE: if( query_yn( _( "Save and quit?" ) ) ) { - if( save() ) { + if( save( true ) ) { u.moves = 0; uquit = QUIT_SAVED; } diff --git a/src/handle_liquid.cpp b/src/handle_liquid.cpp index 1715d6211178..c9fc1856dcfe 100644 --- a/src/handle_liquid.cpp +++ b/src/handle_liquid.cpp @@ -55,8 +55,8 @@ static void serialize_liquid_source( player_activity &act, const tripoint &pos, const auto stack = get_map().i_at( pos ); // Need to store the *index* of the item on the ground, but it may be a virtual item from // an infinite liquid source. - const auto iter = std::find_if( stack.begin(), stack.end(), [&]( const item & i ) { - return &i == &liquid; + const auto iter = std::find_if( stack.begin(), stack.end(), [&]( const item * const & i ) { + return i == &liquid; } ); if( iter == stack.end() ) { act.values.push_back( LST_INFINITE_MAP ); @@ -116,8 +116,8 @@ bool handle_liquid_from_ground( map_stack::iterator on_ground, const int radius ) { // TODO: not all code paths on handle_liquid consume move points, fix that. - handle_liquid( *on_ground, nullptr, radius, &pos ); - if( on_ground->charges > 0 ) { + handle_liquid( **on_ground, nullptr, radius, &pos ); + if( ( *on_ground )->charges > 0 ) { return false; } get_map().i_at( pos ).erase( on_ground ); @@ -141,13 +141,13 @@ bool handle_liquid_from_container( item &container, int radius ) { std::vector remove; bool handled = false; - for( item *contained : container.contents.all_items_top() ) { + for( item *&contained : container.contents.all_items_top() ) { if( handle_liquid_from_container( contained, container, radius ) ) { remove.push_back( contained ); handled = true; } } - for( item *contained : remove ) { + for( item *&contained : remove ) { container.remove_item( *contained ); } return handled; @@ -201,8 +201,8 @@ static bool get_liquid_target( item &liquid, item *const source, const int radiu // This handles containers found anywhere near the player, including on the map and in vehicle storage. menu.addentry( -1, true, 'c', _( "Pour into a container" ) ); actions.emplace_back( [&]() { - target.item_loc = game_menus::inv::container_for( g->u, liquid, radius ); - item *const cont = target.item_loc.get_item(); + target.it = game_menus::inv::container_for( g->u, liquid, radius ); + item *const cont = target.it; if( cont == nullptr || cont->is_null() ) { add_msg( _( "Never mind." ) ); @@ -223,7 +223,7 @@ static bool get_liquid_target( item &liquid, item *const source, const int radiu if( veh ) { vehicle_part_range vpr = veh->get_all_parts(); if( veh && std::any_of( vpr.begin(), vpr.end(), [&liquid]( const vpart_reference & pt ) { - return pt.part().can_reload( liquid ); + return pt.part().can_reload( &liquid ); } ) ) { opts.insert( veh ); } @@ -337,7 +337,7 @@ static bool perform_liquid_transfer( item &liquid, const tripoint *const source_ transfer_ok = true; break; case LD_ITEM: { - item *const cont = target.item_loc.get_item(); + item *const cont = target.it; const int item_index = g->u.get_item_position( cont ); // Currently activities can only store item position in the players inventory, // not on ground or similar. TODO: implement storing arbitrary container locations. @@ -346,16 +346,16 @@ static bool perform_liquid_transfer( item &liquid, const tripoint *const source_ } else if( g->u.pour_into( *cont, liquid ) ) { if( cont->needs_processing() ) { // Polymorphism fail, have to introspect into the type to set the target container as active. - switch( target.item_loc.where() ) { - case item_location::type::map: - here.make_active( target.item_loc ); + switch( target.it->where() ) { + case item::item_location_type::map: + here.make_active( *target.it ); break; - case item_location::type::vehicle: - here.veh_at( target.item_loc.position() )->vehicle().make_active( target.item_loc ); + case item::item_location_type::vehicle: + here.veh_at( target.it->position() )->vehicle().make_active( *target.it ); break; - case item_location::type::container: - case item_location::type::character: - case item_location::type::invalid: + case item::item_location_type::container: + case item::item_location_type::character: + case item::item_location_type::invalid: break; } } diff --git a/src/handle_liquid.h b/src/handle_liquid.h index 36230171de05..849c28960280 100644 --- a/src/handle_liquid.h +++ b/src/handle_liquid.h @@ -23,7 +23,7 @@ enum liquid_dest : int { struct liquid_dest_opt { liquid_dest dest_opt = LD_NULL; tripoint pos; - item_location item_loc; + item *it; vehicle *veh = nullptr; }; @@ -42,7 +42,7 @@ namespace liquid_handler * charges of the liquid have been transferred. * `true` indicates some charges have been transferred (but not necessarily all of them). */ -void handle_all_liquid( item liquid, int radius ); +void handle_all_liquid( item &liquid, int radius ); /** * Consume / handle as much of the liquid as possible in varying ways. This function can diff --git a/src/iexamine.cpp b/src/iexamine.cpp index e7c3cc43004b..1986c1ea8603 100644 --- a/src/iexamine.cpp +++ b/src/iexamine.cpp @@ -321,13 +321,14 @@ void iexamine::nanofab( player &p, const tripoint &examp ) return; } - item new_item( nanofab_template->get_var( "NANOFAB_ITEM_ID" ), calendar::turn ); + item &new_item = *item::spawn( nanofab_template->get_var( "NANOFAB_ITEM_ID" ), calendar::turn ); auto qty = std::max( 1, new_item.volume() / 250_ml ); auto reqs = *requirement_id( "nanofabricator" ) * qty; if( !reqs.can_make_with_inventory( p.crafting_inventory(), is_crafting_component ) ) { popup( "%s", reqs.list_missing() ); + new_item.destroy(); return; } @@ -361,18 +362,19 @@ void iexamine::gaspump( player &p, const tripoint &examp ) auto items = here.i_at( examp ); for( auto item_it = items.begin(); item_it != items.end(); ++item_it ) { - if( item_it->made_of( LIQUID ) ) { + item *content = *item_it; + if( content->made_of( LIQUID ) ) { ///\EFFECT_DEX decreases chance of spilling gas from a pump if( one_in( 10 + p.get_dex() ) ) { - add_msg( m_bad, _( "You accidentally spill the %s." ), item_it->type_name() ); + add_msg( m_bad, _( "You accidentally spill the %s." ), content->type_name() ); static const auto max_spill_volume = units::from_liter( 1 ); - const int max_spill_charges = std::max( 1, item_it->charges_per_volume( max_spill_volume ) ); + const int max_spill_charges = std::max( 1, content->charges_per_volume( max_spill_volume ) ); ///\EFFECT_DEX decreases amount of gas spilled from a pump const int qty = rng( 1, max_spill_charges * 8.0 / std::max( 1, p.get_dex() ) ); - item spill = item_it->split( qty ); + item &spill = content->split( qty ); if( spill.is_null() ) { - here.add_item_or_charges( p.pos(), *item_it ); + here.add_item_or_charges( p.pos(), *content ); items.erase( item_it ); } else { here.add_item_or_charges( p.pos(), spill ); @@ -545,7 +547,7 @@ class atm_menu return false; } - item card( "cash_card", calendar::turn ); + item &card = *item::spawn( "cash_card", calendar::turn ); card.charges = 0; u.i_add( card ); u.cash -= 1000; @@ -588,12 +590,12 @@ class atm_menu int pos = u.inv.position_by_type( itype_cash_card ); item *dst; if( pos == INT_MIN ) { - dst = &u.weapon; + dst = &u.get_weapon(); } else { dst = &u.i_at( pos ); } - if( dst->is_null() ) { + if( dst==nullptr || dst->is_null() ) { //Just in case we run into an edge case popup( _( "You do not have a cash card to withdraw money!" ) ); return false; @@ -620,8 +622,9 @@ class atm_menu item *dst; if( u.activity.id() == ACT_ATM ) { u.activity.set_to_null(); // stop for now, if required, it will be created again. - dst = u.activity.targets.front().get_item(); - if( dst->is_null() || dst->typeId() != itype_cash_card ) { + //TODO!: safety check? + dst = &*u.activity.targets.front(); + if( dst==nullptr || dst->is_null() || dst->typeId() != itype_cash_card ) { return false; } } else { @@ -642,7 +645,7 @@ class atm_menu // the next turn. Putting this here makes sure there will be something to be // done next turn. u.assign_activity( ACT_ATM, 0, transfer_all_money ); - u.activity.targets.push_back( item_location( u, dst ) ); + u.activity.targets.push_back( dst ); break; } @@ -732,7 +735,7 @@ void iexamine::vending( player &p, const tripoint &examp ) for( auto it = std::begin( vend_items ); it != std::end( vend_items ); ++it ) { // |# {name}| // 123 4 - item_map[it->tname()].push_back( it ); + item_map[( *it )->tname()].push_back( it ); } // Next, put pointers to the pairs in the map in a vector to allow indexing. @@ -786,13 +789,14 @@ void iexamine::vending( player &p, const tripoint &examp ) werase( w_item_info ); // | {line}| // 12 3 - fold_and_print( w_item_info, point( 2, 1 ), w_info_w - 3, c_light_gray, cur_item->info( true ) ); + fold_and_print( w_item_info, point( 2, 1 ), w_info_w - 3, c_light_gray, + ( *cur_item )->info( true ) ); wborder( w_item_info, LINE_XOXO, LINE_XOXO, LINE_OXOX, LINE_OXOX, LINE_OXXO, LINE_OOXX, LINE_XXOO, LINE_XOOX ); //+<{name}>+ //12 34 - const std::string name = utf8_truncate( cur_item->display_name(), + const std::string name = utf8_truncate( ( *cur_item )->display_name(), static_cast( w_info_w - 4 ) ); mvwprintw( w_item_info, point_east, "<%s>", name ); wnoutrefresh( w_item_info ); @@ -813,7 +817,7 @@ void iexamine::vending( player &p, const tripoint &examp ) } else if( action == "UP" ) { cur_pos = ( cur_pos + num_items - 1 ) % num_items; } else if( action == "CONFIRM" ) { - const int iprice = cur_item->price( false ); + const int iprice = ( *cur_item )->price( false ); if( iprice > money ) { popup( _( "You can't afford that item." ) ); @@ -827,7 +831,7 @@ void iexamine::vending( player &p, const tripoint &examp ) money -= iprice; p.use_charges( itype_cash_card, iprice ); - p.i_add_or_drop( *cur_item ); + p.i_add_or_drop( **cur_item ); vend_items.erase( cur_item ); cur_items.pop_back(); @@ -856,18 +860,18 @@ void iexamine::toilet( player &p, const tripoint &examp ) auto items = get_map().i_at( examp ); auto water = items.begin(); for( ; water != items.end(); ++water ) { - if( water->typeId() == itype_water ) { + if( ( *water )->typeId() == itype_water ) { break; } } if( water == items.end() ) { add_msg( m_info, _( "This toilet is empty." ) ); - } else if( !water->made_of( LIQUID ) ) { + } else if( !( *water )->made_of( LIQUID ) ) { add_msg( m_info, _( "The toilet water is frozen solid!" ) ); } else { // Use a different poison value each time water is drawn from the toilet. - water->poison = one_in( 3 ) ? 0 : rng( 1, 3 ); + ( *water )->poison = one_in( 3 ) ? 0 : rng( 1, 3 ); // TODO: use me ( void ) p; @@ -912,7 +916,7 @@ void iexamine::elevator( player &p, const tripoint &examp ) // TODO: do we have struct or pair to indicate from -> to? const auto move_item = [&]( map_stack & items, const tripoint & src, const tripoint & dest ) { for( auto it = items.begin(); it != items.end(); ) { - here.add_item_or_charges( dest, *it ); + here.add_item_or_charges( dest, **it ); it = here.i_rem( src, it ); } }; @@ -1242,7 +1246,7 @@ void iexamine::deployed_furniture( player &p, const tripoint &pos ) p.add_msg_if_player( m_info, _( "You take down the %s." ), here.furn( pos ).obj().name() ); const auto furn_item = here.furn( pos ).obj().deployed_item; - here.add_item_or_charges( pos, item( furn_item, calendar::turn ) ); + here.add_item_or_charges( pos, *item::spawn( furn_item, calendar::turn ) ); here.furn_set( pos, f_null ); } @@ -1309,7 +1313,7 @@ void iexamine::portable_structure( player &p, const tripoint &examp ) here.furn_set( pt, f_null ); } - here.add_item_or_charges( examp, item( dropped, calendar::turn ) ); + here.add_item_or_charges( examp, *item::spawn( dropped, calendar::turn ) ); } /** @@ -1351,7 +1355,7 @@ void iexamine::pit_covered( player &p, const tripoint &examp ) } map &here = get_map(); - item plank( "2x4", calendar::turn ); + item &plank = *item::spawn( "2x4", calendar::turn ); add_msg( _( "You remove the plank." ) ); here.add_item_or_charges( p.pos(), plank ); @@ -1416,8 +1420,8 @@ void iexamine::slot_machine( player &p, const tripoint & ) void iexamine::safe( player &p, const tripoint &examp ) { auto cracking_tool = p.crafting_inventory().items_with( []( const item & it ) -> bool { - item temporary_item( it.type ); - return temporary_item.has_flag( flag_SAFECRACK ); + //Why not just check the item + return it.has_flag( flag_SAFECRACK ); } ); if( !( !cracking_tool.empty() || p.has_bionic( bio_ears ) ) ) { @@ -1464,7 +1468,7 @@ void iexamine::gunsafe_el( player &p, const tripoint &examp ) } } -static safe_reference find_best_prying_tool( player &p ) +static item * find_best_prying_tool( player &p ) { std::vector prying_items = p.items_with( []( const item & it ) { // we want to get worn items (eg crowbar in toolbelt), so no check on item position @@ -1479,12 +1483,12 @@ static safe_reference find_best_prying_tool( player &p ) // if crowbar() ever eats charges or otherwise alters the passed item, rewrite this to reflect // changes to the original item. if( prying_items.empty() ) { - return safe_reference(); + return nullptr; } - return ( *prying_items[0] ).get_safe_reference(); + return prying_items[0]; } -static safe_reference find_best_lock_picking_tool( player &p ) +static item *find_best_lock_picking_tool( player &p ) { std::vector picklocks = p.items_with( []( const item & it ) { // we want to get worn items (eg hairpin), so no check on item position @@ -1501,10 +1505,10 @@ static safe_reference find_best_lock_picking_tool( player &p ) } ); if( picklocks.empty() ) { - return safe_reference(); + return nullptr; } - return ( *picklocks[0] ).get_safe_reference(); + return picklocks[0]; } static void apply_prying_tool( player &p, item *it, const tripoint &examp ) @@ -1691,7 +1695,7 @@ void iexamine::pedestal_wyrm( player &p, const tripoint &examp ) query_yn( _( "Remove the petrified eye from the pedestal?" ) ) ) { here.i_clear( examp ); - item eye( itype_petrified_eye ); + item &eye = *item::spawn( itype_petrified_eye ); p.i_add_or_drop( eye ); // Send in a few wyrms to start things off. @@ -1875,7 +1879,7 @@ static bool can_drink_nectar( const player &p, const item &nectar ) */ static bool drink_nectar( player &p ) { - item nectar( "nectar", calendar::turn, 1 ); + item &nectar = *item::spawn_temporary( "nectar", calendar::turn, 1 ); if( can_drink_nectar( p, nectar ) ) { p.moves -= to_moves( 30_seconds ); add_msg( _( "You drink some nectar." ) ); @@ -1891,7 +1895,7 @@ static bool drink_nectar( player &p ) */ static void handle_harvest( player &p, const std::string &itemid, bool force_drop ) { - item harvest = item( itemid ); + item &harvest = *item::spawn( itemid ); if( !force_drop && p.can_pick_volume( harvest ) && p.can_pick_weight( harvest, !get_option( "DANGEROUS_PICKUPS" ) ) ) { p.i_add( harvest ); @@ -1916,7 +1920,7 @@ void iexamine::flower_poppy( player &p, const tripoint &examp ) map &here = get_map(); // TODO: Get rid of this section and move it to eating // Two y/n prompts is just too much - item poppy( "nectar", calendar::turn, 1 ); + item &poppy = *item::spawn_temporary( "nectar", calendar::turn, 1 ); if( can_drink_nectar( p, poppy ) ) { if( !query_yn( _( "You feel woozy as you explore the %s. Drink?" ), here.furnname( examp ) ) ) { @@ -2140,7 +2144,7 @@ void iexamine::flower_marloss( player &p, const tripoint &examp ) add_msg( m_info, _( "This flower is still alive, despite the harsh conditions…" ) ); } map &here = get_map(); - item nectar( "nectar" ); + item &nectar = *item::spawn_temporary( "nectar" ); if( can_drink_nectar( p, nectar ) ) { if( !query_yn( _( "You feel out of place as you explore the %s. Drink?" ), here.furnname( examp ) ) ) { @@ -2343,10 +2347,10 @@ void iexamine::dirtmound( player &p, const tripoint &examp ) * @param byproducts If true, byproducts (like straw, withered plants, see * @ref islot_seed::byproducts) are included. */ -std::list iexamine::get_harvest_items( const itype &type, const int plant_count, - const int seed_count, const bool byproducts ) +ItemList iexamine::get_harvest_items( const itype &type, const int plant_count, + const int seed_count, const bool byproducts ) { - std::list result; + ItemList result; if( !type.seed ) { return result; } @@ -2356,16 +2360,18 @@ std::list iexamine::get_harvest_items( const itype &type, const int plant_ const itype_id &seed_type = type.get_id(); const auto add = [&]( const itype_id & id, const int count ) { - item new_item( id, calendar::turn ); + + //TODO!:check + item &new_item = *item::spawn( id, calendar::turn ); if( new_item.count_by_charges() && count > 0 ) { new_item.charges *= count; new_item.charges /= seed_data.fruit_div; if( new_item.charges <= 0 ) { new_item.charges = 1; } - result.push_back( new_item ); + result.push_back( &new_item ); } else if( count > 0 ) { - result.insert( result.begin(), count, new_item ); + result.insert( result.begin(), count, &new_item ); } }; @@ -2392,17 +2398,18 @@ void iexamine::harvest_plant( player &p, const tripoint &examp, bool from_activi map &here = get_map(); // Can't use item_stack::only_item() since there might be fertilizer map_stack items = here.i_at( examp ); - const map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.is_seed(); + const map_stack::iterator seed_it = std::find_if( items.begin(), + items.end(), []( const item * const & it ) { + return it->is_seed(); } ); - if( seed == items.end() ) { + if( seed_it == items.end() ) { debugmsg( "Missing seed for plant at (%d, %d, %d)", examp.x, examp.y, examp.z ); here.i_clear( examp ); here.furn_set( examp, f_null ); return; } - + item *&seed = *seed_it; const itype_id &seedType = seed->typeId(); if( seedType == itype_fungal_seeds ) { fungus( p, examp ); @@ -2444,9 +2451,9 @@ void iexamine::harvest_plant( player &p, const tripoint &examp, bool from_activi const int seedCount = std::max( 1, rng( plant_count / 4, plant_count / 2 ) ); for( auto &i : get_harvest_items( type, plant_count, seedCount, true ) ) { if( from_activity ) { - i.set_var( "activity_var", p.name ); + i->set_var( "activity_var", p.name ); } - here.add_item_or_charges( examp, i ); + here.add_item_or_charges( examp, *i ); } here.furn_set( examp, furn_str_id( here.furn( examp )->plant->transform ) ); p.moves -= to_moves( 10_seconds ); @@ -2480,7 +2487,7 @@ void iexamine::fertilize_plant( player &p, const tripoint &tile, const itype_id return; } - std::list planted = p.use_charges( fertilizer, 1 ); + ItemList planted = p.use_charges( fertilizer, 1 ); // Reduce the amount of time it takes until the next stage of the plant by // 20% of a seasons length. (default 2.8 days). @@ -2489,16 +2496,19 @@ void iexamine::fertilize_plant( player &p, const tripoint &tile, const itype_id map &here = get_map(); // Can't use item_stack::only_item() since there might be fertilizer map_stack items = here.i_at( tile ); - const map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.is_seed(); + const map_stack::iterator seed_it = std::find_if( items.begin(), + items.end(), []( const item * const & it ) { + return it->is_seed(); } ); - if( seed == items.end() ) { + if( seed_it == items.end() ) { debugmsg( "Missing seed for plant at (%d, %d, %d)", tile.x, tile.y, tile.z ); here.i_clear( tile ); here.furn_set( tile, f_null ); return; } + item *&seed = *seed_it; + // TODO: item should probably clamp the value on its own seed->set_birthday( seed->birthday() - fertilizerEpoch ); // The plant furniture has the NOITEM token which prevents adding items on that square, @@ -2512,12 +2522,12 @@ void iexamine::fertilize_plant( player &p, const tripoint &tile, const itype_id //~ %1$s: plant name, %2$s: fertilizer name add_msg( m_info, _( "You fertilize the %1$s with the %2$s." ), seed->get_plant_name(), - planted.front().tname() ); + planted.front()->tname() ); } itype_id iexamine::choose_fertilizer( player &p, const std::string &pname, bool ask_player ) { - std::vector f_inv = p.all_items_with_flag( flag_FERTILIZER ); + std::vector f_inv = p.all_items_with_flag( flag_FERTILIZER ); if( f_inv.empty() ) { add_msg( m_info, _( "You have no fertilizer for the %s." ), pname ); return itype_id(); @@ -2553,17 +2563,20 @@ void iexamine::aggie_plant( player &p, const tripoint &examp ) map &here = get_map(); // Can't use item_stack::only_item() since there might be fertilizer map_stack items = here.i_at( examp ); - const map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.is_seed(); + const map_stack::iterator seed_it = std::find_if( items.begin(), + items.end(), []( const item * const & it ) { + return it->is_seed(); } ); - if( seed == items.end() ) { + if( seed_it == items.end() ) { debugmsg( "Missing seed for plant at (%d, %d, %d)", examp.x, examp.y, examp.z ); here.i_clear( examp ); here.furn_set( examp, f_null ); return; } + const item *const &seed = *seed_it; + const std::string pname = seed->get_plant_name(); if( here.has_flag_furn( flag_GROWTH_HARVEST, examp ) && @@ -2601,15 +2614,15 @@ void iexamine::kiln_empty( player &p, const tripoint &examp ) static const std::set kilnable{ material_id( "wood" ), material_id( "bone" ) }; bool fuel_present = false; auto items = here.i_at( examp ); - for( const item &i : items ) { - if( i.typeId() == itype_charcoal ) { + for( const item * const &i : items ) { + if( i->typeId() == itype_charcoal ) { add_msg( _( "This kiln already contains charcoal." ) ); add_msg( _( "Remove it before firing the kiln again." ) ); return; - } else if( i.made_of_any( kilnable ) ) { + } else if( i->made_of_any( kilnable ) ) { fuel_present = true; } else { - add_msg( m_bad, _( "This kiln contains %s, which can't be made into charcoal!" ), i.tname( 1, + add_msg( m_bad, _( "This kiln contains %s, which can't be made into charcoal!" ), i->tname( 1, false ) ); return; } @@ -2627,8 +2640,8 @@ void iexamine::kiln_empty( player &p, const tripoint &examp ) // Burn stuff that should get charred, leave out the rest units::volume total_volume = 0_ml; - for( const item &i : items ) { - total_volume += i.volume(); + for( const item * const &i : items ) { + total_volume += i->volume(); } units::volume char_volume = ( 100 - loss ) * total_volume / 100; @@ -2652,7 +2665,7 @@ void iexamine::kiln_empty( player &p, const tripoint &examp ) p.use_charges( itype_fire, 1 ); here.i_clear( examp ); here.furn_set( examp, next_kiln_type ); - item result( itype_unfinished_charcoal, calendar::turn ); + item &result = *item::spawn( itype_unfinished_charcoal, calendar::turn ); result.charges = char_charges; here.add_item( examp, result ); add_msg( _( "You fire the charcoal kiln." ) ); @@ -2700,15 +2713,16 @@ void iexamine::kiln_full( player &, const tripoint &examp ) units::volume total_volume = 0_ml; // Burn stuff that should get charred, leave out the rest for( auto item_it = items.begin(); item_it != items.end(); ) { - if( item_it->typeId() == itype_unfinished_charcoal || item_it->typeId() == itype_charcoal ) { - total_volume += item_it->volume(); + if( ( *item_it )->typeId() == itype_unfinished_charcoal || + ( *item_it )->typeId() == itype_charcoal ) { + total_volume += ( *item_it )->volume(); item_it = items.erase( item_it ); } else { item_it++; } } - - item result( itype_charcoal, calendar::turn ); + //TODO!: check + item &result = *item::spawn( itype_charcoal, calendar::turn ); result.charges = itype_charcoal->charges_per_volume( total_volume ); here.add_item( examp, result ); here.furn_set( examp, next_kiln_type ); @@ -2731,16 +2745,16 @@ void iexamine::arcfurnace_empty( player &p, const tripoint &examp ) static const std::set arcfurnaceable{ material_id( "cac2powder" ) }; bool fuel_present = false; auto items = here.i_at( examp ); - for( const item &i : items ) { - if( i.typeId() == itype_chem_carbide ) { + for( const item * const &i : items ) { + if( i->typeId() == itype_chem_carbide ) { add_msg( _( "This furnace already contains calcium carbide." ) ); add_msg( _( "Remove it before activating the arc furnace again." ) ); return; - } else if( i.made_of_any( arcfurnaceable ) ) { + } else if( i->made_of_any( arcfurnaceable ) ) { fuel_present = true; } else { add_msg( m_bad, _( "This furnace contains %s, which can't be made into calcium carbide!" ), - i.tname( 1, false ) ); + i->tname( 1, false ) ); return; } } @@ -2757,8 +2771,8 @@ void iexamine::arcfurnace_empty( player &p, const tripoint &examp ) // Burn stuff that should get charred, leave out the rest units::volume total_volume = 0_ml; - for( const item &i : items ) { - total_volume += i.volume(); + for( const item * const &i : items ) { + total_volume += i->volume(); } units::volume char_volume = ( 100 - loss ) * total_volume / 100; @@ -2782,7 +2796,7 @@ void iexamine::arcfurnace_empty( player &p, const tripoint &examp ) p.use_charges( itype_UPS, 1250 ); here.i_clear( examp ); here.furn_set( examp, next_arcfurnace_type ); - item result( itype_unfinished_cac2, calendar::turn ); + item &result = *item::spawn( itype_unfinished_cac2, calendar::turn ); result.charges = char_charges; here.add_item( examp, result ); add_msg( _( "You turn on the furnace." ) ); @@ -2828,15 +2842,16 @@ void iexamine::arcfurnace_full( player &, const tripoint &examp ) units::volume total_volume = 0_ml; // Burn stuff that should get charred, leave out the rest for( auto item_it = items.begin(); item_it != items.end(); ) { - if( item_it->typeId() == itype_unfinished_cac2 || item_it->typeId() == itype_chem_carbide ) { - total_volume += item_it->volume(); + if( ( *item_it )->typeId() == itype_unfinished_cac2 || + ( *item_it )->typeId() == itype_chem_carbide ) { + total_volume += ( *item_it )->volume(); item_it = items.erase( item_it ); } else { item_it++; } } - item result( itype_chem_carbide, calendar::turn ); + item &result = *item::spawn( itype_chem_carbide, calendar::turn ); result.charges = itype_chem_carbide->charges_per_volume( total_volume ); here.add_item( examp, result ); here.furn_set( examp, next_arcfurnace_type ); @@ -2846,9 +2861,9 @@ void iexamine::arcfurnace_full( player &, const tripoint &examp ) void iexamine::autoclave_empty( player &p, const tripoint & ) { - item_location bionic = game_menus::inv::sterilize_cbm( p ); + item* bionic = game_menus::inv::sterilize_cbm( p ); if( bionic ) { - p.mend_item( item_location( bionic ) ); + p.mend_item( *bionic ); } else { add_msg( _( "Never mind." ) ); } @@ -2868,12 +2883,12 @@ void iexamine::autoclave_full( player &, const tripoint &examp ) } map_stack items = here.i_at( examp ); - bool cbms = std::all_of( items.begin(), items.end(), []( const item & i ) { - return i.is_bionic(); + bool cbms = std::all_of( items.begin(), items.end(), []( const item * const & i ) { + return i->is_bionic(); } ); - bool cbms_not_packed = std::all_of( items.begin(), items.end(), []( const item & i ) { - return i.is_bionic() && i.has_flag( flag_NO_PACKED ); + bool cbms_not_packed = std::all_of( items.begin(), items.end(), []( const item * const & i ) { + return i->is_bionic() && i->has_flag( flag_NO_PACKED ); } ); if( items.empty() ) { @@ -2888,7 +2903,7 @@ void iexamine::autoclave_full( player &, const tripoint &examp ) } add_msg( _( "The autoclave is running." ) ); - const item &clock = *items.begin(); + const item &clock = **items.begin(); const time_duration Cycle_time = 90_minutes; const time_duration time_left = Cycle_time - clock.age(); @@ -2898,9 +2913,9 @@ void iexamine::autoclave_full( player &, const tripoint &examp ) } here.furn_set( examp, next_autoclave_type ); - for( item &it : items ) { - if( !it.has_flag( flag_NO_PACKED ) ) { - it.unset_flag( flag_NO_STERILE ); + for( item * const &it : items ) { + if( !it->has_flag( flag_NO_PACKED ) ) { + it->unset_flag( flag_NO_STERILE ); } } add_msg( m_good, _( "The cycle is complete, the CBMs are now sterile." ) ); @@ -2993,7 +3008,7 @@ void iexamine::fireplace( player &p, const tripoint &examp ) p.add_msg_if_player( m_info, _( "You take down the %s." ), here.furnname( examp ) ); const auto furn_item = here.furn( examp ).obj().deployed_item; - here.add_item_or_charges( examp, item( furn_item, calendar::turn ) ); + here.add_item_or_charges( examp, *item::spawn( furn_item, calendar::turn ) ); here.furn_set( examp, f_null ); return; } @@ -3023,10 +3038,11 @@ void iexamine::fvat_empty( player &p, const tripoint &examp ) map &here = get_map(); auto items = here.i_at( examp ); for( auto item_it = items.begin(); item_it != items.end(); ) { - if( !item_it->is_brewable() || brew_present ) { + //TODO!: check + if( !( *item_it )->is_brewable() || brew_present ) { // This isn't a brew or there was already another kind of brew inside, // so this has to be moved. - items.insert( *item_it ); + items.insert( **item_it ); // This will add items to a space near the vat, because it's flagged as NOITEM. item_it = items.erase( item_it ); } else { @@ -3104,7 +3120,7 @@ void iexamine::fvat_empty( player &p, const tripoint &examp ) } } if( to_deposit ) { - item brew( brew_type, calendar::start_of_cataclysm ); + item &brew = *item::spawn( brew_type, calendar::start_of_cataclysm ); int charges_held = p.charges_of( brew_type ); brew.charges = charges_on_ground; for( int i = 0; i < charges_held && !vat_full; i++ ) { @@ -3146,11 +3162,11 @@ void iexamine::fvat_full( player &p, const tripoint &examp ) return; } - for( item &it : items_here ) { - if( !it.made_of( LIQUID ) ) { - add_msg( _( "You remove %s from the vat." ), it.tname() ); - here.add_item_or_charges( p.pos(), it ); - here.i_rem( examp, &it ); + for( item * const &it : items_here ) { + if( !it->made_of( LIQUID ) ) { + add_msg( _( "You remove %s from the vat." ), it->tname() ); + here.add_item_or_charges( p.pos(), *it ); + here.i_rem( examp, it ); } } @@ -3159,7 +3175,7 @@ void iexamine::fvat_full( player &p, const tripoint &examp ) return; } - item &brew_i = *items_here.begin(); + item &brew_i = **items_here.begin(); // Does the vat contain unfermented brew, or already fermented booze? // TODO: Allow "recursive brewing" to continue without player having to check on it if( brew_i.is_brewable() ) { @@ -3186,7 +3202,7 @@ void iexamine::fvat_full( player &p, const tripoint &examp ) here.i_clear( examp ); for( const auto &result : results ) { // TODO: Different age based on settings - item booze( result, brew_i.birthday(), brew_i.charges ); + item &booze = *item::spawn( result, brew_i.birthday(), brew_i.charges ); here.add_item( examp, booze ); if( booze.made_of( LIQUID ) ) { add_msg( _( "The %s is now ready for bottling." ), booze.tname() ); @@ -3233,11 +3249,12 @@ static void displace_items_except_one_liquid( const tripoint &examp ) bool liquid_present = false; map_stack items = here.i_at( examp ); for( map_stack::iterator it = items.begin(); it != items.end(); ) { - if( !it->made_of( LIQUID ) || liquid_present ) { + //TODO!:check + if( !( *it )->made_of( LIQUID ) || liquid_present ) { // This isn't a liquid or there was already another kind of liquid inside, // so this has to be moved. // This will add items to a space near the vat, because it's flagged as NOITEM. - items.insert( *it ); + items.insert( **it ); it = items.erase( it ); } else { it++; @@ -3312,7 +3329,7 @@ void iexamine::keg( player &p, const tripoint &examp ) //Store liquid chosen in the keg itype_id drink_type = drink_types[ drink_index ]; int charges_held = p.charges_of( drink_type ); - item drink( drink_type, calendar::start_of_cataclysm ); + item &drink = *item::spawn( drink_type, calendar::start_of_cataclysm ); drink.set_relative_rot( drink_rot[ drink_index ] ); drink.charges = 0; bool keg_full = false; @@ -3389,8 +3406,10 @@ void iexamine::keg( player &p, const tripoint &examp ) drink_nname, keg_name ); return; } - item tmp( drink.typeId(), calendar::turn, charges_held ); - pour_into_keg( examp, tmp ); + item &tmp = *item::spawn( drink.typeId(), calendar::turn, charges_held ); + if( !pour_into_keg( examp, tmp ) ) { + tmp.destroy(); + } p.use_charges( drink.typeId(), charges_held - tmp.charges ); add_msg( _( "You fill the %1$s with %2$s." ), keg_name, drink_nname ); p.moves -= to_moves( 10_seconds ); @@ -3412,7 +3431,7 @@ void iexamine::keg( player &p, const tripoint &examp ) /** * Pour liquid into a keg (furniture) on the map. The transferred charges (if any) * will be removed from the liquid item. - * @return Whether any charges have been transferred at all. + * @return Whether liquid has been placed on the map. */ bool iexamine::pour_into_keg( const tripoint &pos, item &liquid ) { @@ -3423,10 +3442,12 @@ bool iexamine::pour_into_keg( const tripoint &pos, item &liquid ) map &here = get_map(); const auto keg_name = here.name( pos ); + bool added = false; map_stack stack = here.i_at( pos ); if( stack.empty() ) { here.add_item( pos, liquid ); here.i_at( pos ).only_item().charges = 0; // Will be set later + added = true; } else if( stack.only_item().typeId() != liquid.typeId() ) { add_msg( _( "The %s already contains some %s, you can't add a different liquid to it." ), keg_name, item::nname( stack.only_item().typeId() ) ); @@ -3436,7 +3457,7 @@ bool iexamine::pour_into_keg( const tripoint &pos, item &liquid ) item &drink = stack.only_item(); if( drink.volume() >= keg_cap ) { add_msg( _( "The %s is full." ), keg_name ); - return false; + return added; } add_msg( _( "You pour %1$s into the %2$s." ), liquid.tname(), keg_name ); @@ -3444,7 +3465,7 @@ bool iexamine::pour_into_keg( const tripoint &pos, item &liquid ) drink.charges++; liquid.charges--; } - return true; + return added; } static void pick_plant( player &p, const tripoint &examp, @@ -3501,9 +3522,9 @@ void iexamine::tree_hickory( player &p, const tripoint &examp ) p.moves -= to_moves( 20_seconds ) / ( p.get_skill_level( skill_survival ) + 1 ) + 100; } -static item_location maple_tree_sap_container() +static item* maple_tree_sap_container() { - const item maple_sap = item( itype_maple_sap, calendar::start_of_cataclysm ); + const item &maple_sap = *item::spawn_temporary( itype_maple_sap, calendar::start_of_cataclysm ); return g->inv_map_splice( [&]( const item & it ) { return it.get_remaining_capacity_for_liquid( maple_sap, true ) > 0; }, _( "Which container?" ), PICKUP_RANGE ); @@ -3538,13 +3559,12 @@ void iexamine::tree_maple( player &p, const tripoint &examp ) map &here = get_map(); here.ter_set( examp, t_tree_maple_tapped ); - auto cont_loc = maple_tree_sap_container(); - - item *container = cont_loc.get_item(); + item *container = maple_tree_sap_container(); if( container ) { + + container->detach(); here.add_item_or_charges( examp, *container, false ); - cont_loc.remove_item(); } else { add_msg( m_info, _( "No container added. The sap will just spill on the ground." ) ); } @@ -3596,7 +3616,7 @@ void iexamine::tree_maple_tapped( player &p, const tripoint &examp ) return; } - item tree_spile( "tree_spile" ); + item &tree_spile = *item::spawn( "tree_spile" ); add_msg( _( "You remove the %s." ), tree_spile.tname( 1 ) ); here.add_item_or_charges( p.pos(), tree_spile ); @@ -3612,13 +3632,11 @@ void iexamine::tree_maple_tapped( player &p, const tripoint &examp ) } case ADD_CONTAINER: { - auto cont_loc = maple_tree_sap_container(); - - container = cont_loc.get_item(); + container = maple_tree_sap_container(); if( container ) { + container->detach(); here.add_item_or_charges( examp, *container, false ); - cont_loc.remove_item(); } else { add_msg( m_info, _( "No container added. The sap will just spill on the ground." ) ); } @@ -3633,7 +3651,7 @@ void iexamine::tree_maple_tapped( player &p, const tripoint &examp ) case REMOVE_CONTAINER: { g->u.assign_activity( player_activity( pickup_activity_actor( - { { item_location( map_cursor( examp ), container ), cata::nullopt, {} } }, g->u.pos() ) ) ); + { { container, cata::nullopt, {} } }, g->u.pos() ) ) ); return; } @@ -3726,19 +3744,19 @@ void iexamine::recycle_compactor( player &, const tripoint &examp ) std::set accepts( ca.begin(), ca.end() ); accepts.insert( m.id ); for( auto &input : inputs ) { - if( !input.only_made_of( accepts ) ) { + if( !input->only_made_of( accepts ) ) { //~ %1$s: an item in the compactor , %2$s: desired compactor output material add_msg( _( "You realize this isn't going to work because %1$s is not made purely of %2$s." ), - input.tname(), m.name() ); + input->tname(), m.name() ); return; } - if( input.is_container() && !input.is_container_empty() ) { + if( input->is_container() && !input->is_container_empty() ) { //~ %1$s: an item in the compactor add_msg( _( "You realize this isn't going to work because %1$s has not been emptied of its contents." ), - input.tname() ); + input->tname() ); return; } - sum_weight += input.weight(); + sum_weight += input->weight(); } if( sum_weight <= 0_gram ) { //~ %1$s: desired compactor output material @@ -3758,11 +3776,12 @@ void iexamine::recycle_compactor( player &, const tripoint &examp ) choose_output.text = string_format( _( "Compact %1$.3f %2$s of %3$s into:" ), convert_weight( sum_weight ), weight_units(), m.name() ); for( auto &ci : m.compacts_into() ) { - auto it = item( ci, calendar::start_of_cataclysm, item::solitary_tag{} ); - const int amount = norm_recover_weight / it.weight(); + //TODO!: check + auto it = item::spawn_temporary( ci, calendar::start_of_cataclysm, item::solitary_tag{} ); + const int amount = norm_recover_weight / it->weight(); //~ %1$d: number of, %2$s: output item choose_output.addentry( string_format( _( "about %1$d %2$s" ), amount, - it.tname( amount ) ) ); + it->tname( amount ) ) ); } choose_output.query(); int o_idx = choose_output.ret; @@ -3783,7 +3802,7 @@ void iexamine::recycle_compactor( player &, const tripoint &examp ) bool out_desired = false; bool out_any = false; for( auto it = m.compacts_into().begin() + o_idx; it != m.compacts_into().end(); ++it ) { - const units::mass ow = item( *it, calendar::start_of_cataclysm, item::solitary_tag{} ).weight(); + const units::mass ow = item::spawn_temporary( *it, calendar::start_of_cataclysm, item::solitary_tag{} )->weight(); int count = sum_weight / ow; sum_weight -= count * ow; if( count > 0 ) { @@ -3832,8 +3851,8 @@ void iexamine::trap( player &p, const tripoint &examp ) built.group->name(), pc->counter / 100000 ) ) { if( query_yn( _( "Cancel construction?" ) ) ) { here.disarm_trap( examp ); - for( const item &it : pc->components ) { - here.add_item_or_charges( p.pos(), it ); + for( item *&it : pc->components ) { + here.add_item_or_charges( p.pos(), *it ); } here.partial_con_remove( examp ); return; @@ -3868,7 +3887,8 @@ void iexamine::trap( player &p, const tripoint &examp ) void iexamine::water_source( player &p, const tripoint &examp ) { map &here = get_map(); - item water = here.water_from( examp ); + //TODO!: check this + item &water = here.water_from( examp ); // TODO: use me ( void ) p; liquid_handler::handle_liquid( water, nullptr, 0, &examp ); @@ -3876,8 +3896,8 @@ void iexamine::water_source( player &p, const tripoint &examp ) void iexamine::clean_water_source( player &, const tripoint &examp ) { - item water = item( "water_clean", calendar::start_of_cataclysm, item::INFINITE_CHARGES ); - liquid_handler::handle_liquid( water, nullptr, 0, &examp ); + item *water = item::spawn( "water_clean", calendar::start_of_cataclysm, item::INFINITE_CHARGES ); + liquid_handler::handle_liquid( *water, nullptr, 0, &examp ); } std::vector furn_t::crafting_pseudo_item_types() const @@ -3908,8 +3928,8 @@ std::vector furn_t::crafting_ammo_item_types() const static int count_charges_in_list( const itype *type, const map_stack &items ) { for( const auto &candidate : items ) { - if( candidate.type == type ) { - return candidate.charges; + if( candidate->type == type ) { + return candidate->charges; } } return 0; @@ -3963,9 +3983,9 @@ void iexamine::reload_furniture( player &p, const tripoint &examp ) cur_ammo->nname( amount_in_furn ) ) ) { auto items = here.i_at( examp ); for( auto &itm : items ) { - if( itm.type == cur_ammo ) { + if( itm->type == cur_ammo ) { g->u.assign_activity( player_activity( pickup_activity_actor( - { { item_location( map_cursor( examp ), &itm ), cata::nullopt, {} } }, g->u.pos() ) ) ); + { { itm, cata::nullopt, {} } }, g->u.pos() ) ) ); return; } } @@ -4001,15 +4021,15 @@ void iexamine::reload_furniture( player &p, const tripoint &examp ) p.use_charges( cur_ammo->get_id(), amount ); auto items = here.i_at( examp ); for( auto &itm : items ) { - if( itm.type == cur_ammo ) { - itm.charges += amount; + if( itm->type == cur_ammo ) { + itm->charges += amount; amount = 0; break; } } if( amount != 0 ) { - item it( cur_ammo->get_id(), calendar::turn, amount ); - here.add_item( examp, it ); + item *it = item::spawn( cur_ammo->get_id(), calendar::turn, amount ); + here.add_item( examp, *it ); } const int amount_in_furn_after_placing = count_charges_in_list( &ammo_types.at( ammo_index ), @@ -4069,7 +4089,7 @@ void iexamine::use_furn_fake_item( player &p, const tripoint &examp ) } const itype &cur_tool = usable_item_types.at( tool_index ); - item fake_item( cur_tool.get_id(), calendar::turn, 0 ); + item &fake_item = *item::spawn_temporary( cur_tool.get_id(), calendar::turn, 0 ); const itype_id ammo = fake_item.ammo_default(); fake_item.set_flag( "PSEUDO" ); @@ -4192,7 +4212,7 @@ void iexamine::sign( player &p, const tripoint &examp ) // Allow chance to modify message. std::vector tools; - std::vector filter = p.crafting_inventory().items_with( []( const item & it ) { + std::vector filter = p.crafting_inventory().items_with( []( const item & it ) { return it.has_flag( flag_WRITE_MESSAGE ) && it.charges > 0; } ); tools.reserve( filter.size() ); @@ -4263,10 +4283,10 @@ cata::optional iexamine::getNearFilledGasTank( const tripoint ¢er, tank_loc.emplace( tmp ); } for( auto &k : here.i_at( tmp ) ) { - if( k.made_of( LIQUID ) ) { + if( k->made_of( LIQUID ) ) { distance = new_distance; tank_loc.emplace( tmp ); - gas_units = k.charges; + gas_units = k->charges; break; } } @@ -4368,21 +4388,21 @@ bool iexamine::toPumpFuel( const tripoint &src, const tripoint &dst, int units ) map &here = get_map(); auto items = here.i_at( src ); for( auto item_it = items.begin(); item_it != items.end(); ++item_it ) { - if( item_it->made_of( LIQUID ) ) { - if( item_it->charges < units ) { + item *content = *item_it; + if( content->made_of( LIQUID ) ) { + if( content->charges < units ) { return false; } - item_it->charges -= units; - - item liq_d( item_it->type, calendar::turn, units ); + content->charges -= units; + item &liq_d = *item::spawn( content->type, calendar::turn, units ); const auto backup_pump = here.ter( dst ); here.ter_set( dst, ter_str_id::NULL_ID() ); here.add_item_or_charges( dst, liq_d ); here.ter_set( dst, backup_pump ); - if( item_it->charges < 1 ) { + if( content->charges < 1 ) { items.erase( item_it ); } @@ -4398,9 +4418,10 @@ static int fromPumpFuel( const tripoint &dst, const tripoint &src ) map &here = get_map(); auto items = here.i_at( src ); for( auto item_it = items.begin(); item_it != items.end(); ++item_it ) { - if( item_it->made_of( LIQUID ) ) { + item *content = *item_it; + if( content->made_of( LIQUID ) ) { // how much do we have in the pump? - item liq_d( item_it->type, calendar::turn, item_it->charges ); + item &liq_d = *item::spawn( content->type, calendar::turn, content->charges ); // add the charges to the destination const auto backup_tank = here.ter( dst ); @@ -4409,7 +4430,7 @@ static int fromPumpFuel( const tripoint &dst, const tripoint &src ) here.ter_set( dst, backup_tank ); // remove the liquid from the pump - int amount = item_it->charges; + int amount = content->charges; items.erase( item_it ); return amount; } @@ -4773,14 +4794,14 @@ static Character &operator_present( Character &p, const tripoint &autodoc_loc, return null_patient; } -static item &cyborg_on_couch( const tripoint &couch_pos, item &null_cyborg ) +static item *cyborg_on_couch( const tripoint &couch_pos ) { - for( item &it : get_map().i_at( couch_pos ) ) { - if( it.typeId() == itype_bot_broken_cyborg || it.typeId() == itype_bot_prototype_cyborg ) { + for( item * const &it : get_map().i_at( couch_pos ) ) { + if( it->typeId() == itype_bot_broken_cyborg || it->typeId() == itype_bot_prototype_cyborg ) { return it; } - if( it.typeId() == itype_corpse ) { - if( it.get_mtype()->id == mon_broken_cyborg || it.get_mtype()->id == mon_prototype_cyborg ) { + if( it->typeId() == itype_corpse ) { + if( it->get_mtype()->id == mon_broken_cyborg || it->get_mtype()->id == mon_prototype_cyborg ) { return it; } } @@ -4790,18 +4811,18 @@ static item &cyborg_on_couch( const tripoint &couch_pos, item &null_cyborg ) flag_AUTODOC_COUCH, false ) ) { auto dest_veh = &vp->vehicle(); int dest_part = vp->part_index(); - for( item &it : dest_veh->get_items( dest_part ) ) { - if( it.typeId() == itype_bot_broken_cyborg || it.typeId() == itype_bot_prototype_cyborg ) { + for( item * const &it : dest_veh->get_items( dest_part ) ) { + if( it->typeId() == itype_bot_broken_cyborg || it->typeId() == itype_bot_prototype_cyborg ) { return it; } - if( it.typeId() == itype_corpse ) { - if( it.get_mtype()->id == mon_broken_cyborg || it.get_mtype()->id == mon_prototype_cyborg ) { + if( it->typeId() == itype_corpse ) { + if( it->get_mtype()->id == mon_broken_cyborg || it->get_mtype()->id == mon_prototype_cyborg ) { return it; } } } } - return null_cyborg; + return nullptr; } static player &best_installer( player &p, player &null_player, int difficulty ) @@ -4883,19 +4904,19 @@ void iexamine::autodoc( player &p, const tripoint &examp ) player &patient = player_on_couch( p, examp, null_player, adjacent_couch, couch_pos ); Character &Operator = operator_present( p, examp, null_player ); - static item null_cyborg; - item &cyborg = cyborg_on_couch( couch_pos, null_cyborg ); + //static item null_cyborg; + item *cyborg = cyborg_on_couch( couch_pos ); if( !adjacent_couch ) { popup( _( "No connected couches found. Operation impossible. Exiting." ) ); return; } if( &patient == &null_player ) { - if( &cyborg != &null_cyborg ) { - if( cyborg.typeId() == itype_corpse && !cyborg.active ) { + if( cyborg != nullptr ) { + if( cyborg->typeId() == itype_corpse && !cyborg->active ) { popup( _( "Patient is dead. Please remove corpse to proceed. Exiting." ) ); return; - } else if( cyborg.typeId() == itype_bot_broken_cyborg || cyborg.typeId() == itype_corpse ) { + } else if( cyborg->typeId() == itype_bot_broken_cyborg || cyborg->typeId() == itype_corpse ) { popup( _( "ERROR Bionic Level Assessement: FULL CYBORG. Autodoc Mk. XI can't opperate. Please move patient to appropriate facility. Exiting." ) ); return; } @@ -4919,7 +4940,7 @@ void iexamine::autodoc( player &p, const tripoint &examp ) } int choice_index = uilist( _( "Choose bionic to uninstall" ), choice_names ); if( choice_index == 0 ) { - g->save_cyborg( &cyborg, couch_pos, p ); + g->save_cyborg( cyborg, couch_pos, p ); } else { popup( _( "UNKNOWN COMMAND. Autodoc Mk. XI. Crashed." ) ); return; @@ -4952,15 +4973,15 @@ void iexamine::autodoc( player &p, const tripoint &examp ) _( "\n Using the Autodoc without an operator can lead to serious injuries and various internal bionic malfunctions.\n Manufacturer guarantees automated bionic installation in functional condition.\n Manufacturer does not guarantee automated bionic uninstallation.\n By continuing with the operation you accept the risks and acknowledge that you will not take any legal actions against this facility in case of an accident.\n\nThe following skills affect autodoc installation: Computers, First Aid, and Electronics." ); } - std::vector arm_splints; - std::vector leg_splints; + std::vector arm_splints; + std::vector leg_splints; // find splints on the ground - for( const item &supplies : get_map().i_at( examp ) ) { - if( supplies.typeId() == itype_arm_splint ) { + for( item *&supplies : get_map().i_at( examp ) ) { + if( supplies->typeId() == itype_arm_splint ) { arm_splints.push_back( supplies ); } - if( supplies.typeId() == itype_leg_splint ) { + if( supplies->typeId() == itype_leg_splint ) { leg_splints.push_back( supplies ); } } @@ -4969,11 +4990,11 @@ void iexamine::autodoc( player &p, const tripoint &examp ) flag_AUTODOC, false ) ) { auto dest_veh = &vp->vehicle(); int dest_part = vp->part_index(); - for( item &it : dest_veh->get_items( dest_part ) ) { - if( it.typeId() == itype_arm_splint ) { + for( item *&it : dest_veh->get_items( dest_part ) ) { + if( it->typeId() == itype_arm_splint ) { arm_splints.push_back( it ); } - if( it.typeId() == itype_leg_splint ) { + if( it->typeId() == itype_leg_splint ) { leg_splints.push_back( it ); } } @@ -5003,7 +5024,7 @@ void iexamine::autodoc( player &p, const tripoint &examp ) needs_anesthesia = false; } else { const inventory &crafting_inv = p.crafting_inventory(); - std::vector a_filter = crafting_inv.items_with( []( const item & it ) { + std::vector a_filter = crafting_inv.items_with( []( const item & it ) { return it.has_quality( qual_ANESTHESIA ); } ); for( const item *anesthesia_item : a_filter ) { @@ -5015,13 +5036,13 @@ void iexamine::autodoc( player &p, const tripoint &examp ) switch( amenu.ret ) { case INSTALL_CBM: { - item_location bionic = game_menus::inv::install_bionic( p, patient ); + item* bionic = game_menus::inv::install_bionic( p, patient ); if( !bionic ) { return; } - const itype *itemtype = bionic.get_item()->type; + const itype *itemtype = bionic->type; player &installer = best_installer( p, null_player, itemtype->bionic->difficulty ); if( &installer == &null_player ) { @@ -5031,8 +5052,8 @@ void iexamine::autodoc( player &p, const tripoint &examp ) std::vector progs; bool has_install_program = false; - std::vector install_programs = p.crafting_inventory().items_with( [itemtype]( - const item & it ) -> bool { return it.typeId() == itemtype->bionic->installation_data; } ); + std::vector install_programs = p.crafting_inventory().items_with( [itemtype]( + const item & it ) -> bool { return it.typeId() == itemtype->bionic->installation_data; } ); if( !install_programs.empty() ) { has_install_program = true; @@ -5047,7 +5068,8 @@ void iexamine::autodoc( player &p, const tripoint &examp ) if( patient.can_install_bionics( ( *itemtype ), installer, true, has_install_program ? 10 : -1 ) ) { const time_duration duration = itemtype->bionic->difficulty * 20_minutes; patient.introduce_into_anesthesia( duration, installer, needs_anesthesia ); - bionic.remove_item(); + bionic->detach(); + bionic->destroy(); if( needs_anesthesia ) { for( const auto &e : req_anesth.get_components() ) { p.consume_items( e, 1, is_crafting_component ); @@ -5080,7 +5102,7 @@ void iexamine::autodoc( player &p, const tripoint &examp ) bio.id != bio_power_storage_mkII ) { if( bio.info().itype().is_valid() ) { // put cbm items in your inventory - item bionic_to_uninstall( bio.id.str(), calendar::turn ); + item &bionic_to_uninstall = *item::spawn( bio.id.str(), calendar::turn ); bionic_to_uninstall.set_flag( flag_IN_CBM ); bionic_to_uninstall.set_flag( flag_NO_STERILE ); bionic_to_uninstall.set_flag( flag_NO_PACKED ); @@ -5089,14 +5111,14 @@ void iexamine::autodoc( player &p, const tripoint &examp ) } } - const item_location bionic = game_menus::inv::uninstall_bionic( p, patient ); + const item* bionic = game_menus::inv::uninstall_bionic( p, patient ); if( !bionic ) { g->u.remove_items_with( []( const item & it ) {// remove cbm items from inventory return it.has_flag( flag_IN_CBM ); } ); return; } - const item *it = bionic.get_item(); + const item *it = bionic; const itype *itemtype = it->type; const bionic_id &bid = itemtype->bionic->id; @@ -5148,8 +5170,8 @@ void iexamine::autodoc( player &p, const tripoint &examp ) int quantity = 1; if( part == bodypart_id( "arm_l" ) || part == bodypart_id( "arm_r" ) ) { if( !arm_splints.empty() ) { - for( const item &it : get_map().use_amount( examp, 1, itype_arm_splint, quantity ) ) { - patient.wear_item( it, false ); + for( item *&it : get_map().use_amount( examp, 1, itype_arm_splint, quantity ) ) { + patient.wear_item( *it, false ); } } else { popup( _( "Internal supply of arm splints exhausted. Splinting broken arms impossible. Exiting." ) ); @@ -5157,8 +5179,8 @@ void iexamine::autodoc( player &p, const tripoint &examp ) } } else if( part == bodypart_id( "leg_l" ) || part == bodypart_id( "leg_r" ) ) { if( !leg_splints.empty() ) { - for( const item &it : get_map().use_amount( examp, 1, itype_leg_splint, quantity ) ) { - patient.wear_item( it, false ); + for( item *&it : get_map().use_amount( examp, 1, itype_leg_splint, quantity ) ) { + patient.wear_item( *it, false ); } } else { popup( _( "Internal supply of leg splints exhausted. Splinting broken legs impossible. Exiting." ) ); @@ -5316,17 +5338,17 @@ static void mill_activate( player &p, const tripoint &examp ) map_stack items = here.i_at( examp ); units::volume food_volume = 0_ml; - for( item &it : items ) { - if( it.type->milling_data ) { + for( item *&it : items ) { + if( it->type->milling_data ) { food_present = true; - food_volume += it.volume(); + food_volume += it->volume(); continue; } else { - add_msg( m_bad, _( "This mill contains %s, which can't be milled!" ), it.tname( 1, false ) ); - add_msg( _( "You remove the %s from the mill." ), it.tname() ); - here.add_item_or_charges( p.pos(), it ); - p.mod_moves( -p.item_handling_cost( it ) ); - here.i_rem( examp, &it ); + add_msg( m_bad, _( "This mill contains %s, which can't be milled!" ), it->tname( 1, false ) ); + add_msg( _( "You remove the %s from the mill." ), it->tname() ); + here.add_item_or_charges( p.pos(), *it ); + p.mod_moves( -p.item_handling_cost( *it ) ); + here.i_rem( examp, it ); return; } } @@ -5343,14 +5365,14 @@ static void mill_activate( player &p, const tripoint &examp ) } for( auto &it : here.i_at( examp ) ) { - if( it.type->milling_data ) { + if( it->type->milling_data ) { // Do one final rot check before milling, then apply the PROCESSING flag to prevent further checks. - it.process_rot( 1, false, examp, nullptr ); - it.set_flag( flag_PROCESSING ); + it->process_rot( 1, false, examp, nullptr ); + it->set_flag( flag_PROCESSING ); } } here.furn_set( examp, next_mill_type ); - item result( "fake_milling_item", calendar::turn ); + item &result = *item::spawn( "fake_milling_item", calendar::turn ); result.item_counter = to_turns( milling_time ); result.activate(); here.add_item( examp, result ); @@ -5379,31 +5401,31 @@ static void smoker_activate( player &p, const tripoint &examp ) units::volume food_volume = 0_ml; item *charcoal = nullptr; - for( item &it : items ) { - if( it.has_flag( flag_SMOKED ) && !it.has_flag( flag_SMOKABLE ) ) { + for( item * const &it : items ) { + if( it->has_flag( flag_SMOKED ) && !it->has_flag( flag_SMOKABLE ) ) { add_msg( _( "This rack already contains smoked food." ) ); add_msg( _( "Remove it before firing the smoking rack again." ) ); return; } - if( it.has_flag( flag_SMOKABLE ) ) { + if( it->has_flag( flag_SMOKABLE ) ) { food_present = true; - food_volume += it.volume(); + food_volume += it->volume(); continue; } - if( it.typeId() == itype_charcoal ) { + if( it->typeId() == itype_charcoal ) { charcoal_present = true; - charcoal = ⁢ + charcoal = it; } - if( it.typeId() != itype_charcoal && !it.has_flag( flag_SMOKABLE ) ) { - add_msg( m_bad, _( "This rack contains %s, which can't be smoked!" ), it.tname( 1, + if( it->typeId() != itype_charcoal && !it->has_flag( flag_SMOKABLE ) ) { + add_msg( m_bad, _( "This rack contains %s, which can't be smoked!" ), it->tname( 1, false ) ); - add_msg( _( "You remove %s from the rack." ), it.tname() ); - here.add_item_or_charges( p.pos(), it ); - p.mod_moves( -p.item_handling_cost( it ) ); - here.i_rem( examp, &it ); + add_msg( _( "You remove %s from the rack." ), it->tname() ); + here.add_item_or_charges( p.pos(), *it ); + p.mod_moves( -p.item_handling_cost( *it ) ); + here.i_rem( examp, it ); return; } - if( it.has_flag( flag_SMOKED ) && it.has_flag( flag_SMOKABLE ) ) { + if( it->has_flag( flag_SMOKED ) && it->has_flag( flag_SMOKABLE ) ) { add_msg( _( "This rack has some smoked food that might be dehydrated by smoking it again." ) ); } } @@ -5445,18 +5467,19 @@ static void smoker_activate( player &p, const tripoint &examp ) p.use_charges( itype_fire, 1 ); for( auto &it : here.i_at( examp ) ) { - if( it.has_flag( flag_SMOKABLE ) ) { - it.process_rot( 1, false, examp, nullptr ); - it.set_flag( flag_PROCESSING ); + if( it->has_flag( flag_SMOKABLE ) ) { + it->process_rot( 1, false, examp, nullptr ); + it->set_flag( flag_PROCESSING ); } } here.furn_set( examp, next_smoker_type ); if( charcoal->charges == char_charges ) { + //TODO!: check here.i_rem( examp, charcoal ); } else { charcoal->charges -= char_charges; } - item result( "fake_smoke_plume", calendar::turn ); + item &result = *item::spawn( "fake_smoke_plume", calendar::turn ); result.item_counter = to_turns( 6_hours ); result.activate(); here.add_item( examp, result ); @@ -5484,14 +5507,16 @@ void iexamine::mill_finalize( player &, const tripoint &examp, const time_point return; } - for( item &it : items ) { - if( it.type->milling_data ) { - it.calc_rot_while_processing( milling_time ); - const islot_milling &mdata = *it.type->milling_data; - item result( mdata.into_, start_time + milling_time, it.count() * mdata.conversion_rate_ ); + for( item * const &it : items ) { + if( it->type->milling_data ) { + it->calc_rot_while_processing( milling_time ); + const islot_milling &mdata = *it->type->milling_data; + //TODO!: check + item &result = *item::spawn( mdata.into_, start_time + milling_time, + it->count() * mdata.conversion_rate_ ); result.components.push_back( it ); // copied from item::inherit_flags, which can not be called here because it requires a recipe. - for( const std::string &f : it.type->item_tags ) { + for( const std::string &f : it->type->item_tags ) { if( json_flag::get( f ).craft_inherit() ) { result.set_flag( f ); } @@ -5499,9 +5524,10 @@ void iexamine::mill_finalize( player &, const tripoint &examp, const time_point result.recipe_charges = result.charges; // Set flag to tell set_relative_rot() to calc from bday not now result.set_flag( flag_PROCESSING_RESULT ); - result.set_relative_rot( it.get_relative_rot() ); + result.set_relative_rot( it->get_relative_rot() ); result.unset_flag( flag_PROCESSING_RESULT ); - it = result; + //TODO!: restore next + //it = result; } } here.furn_set( examp, next_mill_type ); @@ -5527,36 +5553,38 @@ static void smoker_finalize( player &, const tripoint &examp, const time_point & here.furn_set( examp, next_smoker_type ); return; } - - for( item &it : items ) { - if( it.has_flag( flag_SMOKABLE ) && it.get_comestible() ) { - if( it.get_comestible()->smoking_result.is_empty() ) { - it.unset_flag( flag_PROCESSING ); + //TODO!: check + for( item *&it : items ) { + if( it->has_flag( flag_SMOKABLE ) && it->get_comestible() ) { + if( it->get_comestible()->smoking_result.is_empty() ) { + it->unset_flag( flag_PROCESSING ); } else { - it.calc_rot_while_processing( 6_hours ); + it->calc_rot_while_processing( 6_hours ); - item result( it.get_comestible()->smoking_result, start_time + 6_hours, it.charges ); + item &result = *item::spawn( it->get_comestible()->smoking_result, start_time + 6_hours, + it->charges ); // Set flag to tell set_relative_rot() to calc from bday not now result.set_flag( flag_PROCESSING_RESULT ); - result.set_relative_rot( it.get_relative_rot() ); + result.set_relative_rot( it->get_relative_rot() ); result.unset_flag( flag_PROCESSING_RESULT ); recipe rec; - result.inherit_flags( it, rec ); + result.inherit_flags( *it, rec ); if( !result.has_flag( flag_NUTRIENT_OVERRIDE ) ) { // If the item has "cooks_like" it will be replaced by that item as a component. - if( !it.get_comestible()->cooks_like.is_empty() ) { + if( !it->get_comestible()->cooks_like.is_empty() ) { // Set charges to 1 for stacking purposes. - it = item( it.get_comestible()->cooks_like, it.birthday(), 1 ); + //TODO!: restore next + //it = item( it->get_comestible()->cooks_like, it.birthday(), 1 ); } result.components.push_back( it ); // Smoking is always 1:1, so these must be equal for correct kcal/vitamin calculation. - result.recipe_charges = it.charges; + result.recipe_charges = it->charges; result.set_flag_recursive( flag_COOKED ); } - - it = result; + //TODO!: restore next + // it = result; } } } @@ -5579,7 +5607,7 @@ static void smoker_load_food( player &p, const tripoint &examp, inv.remove_items_with( []( const item & it ) { return it.rotten(); } ); - std::vector filtered = p.crafting_inventory().items_with( []( const item & it ) { + std::vector filtered = p.crafting_inventory().items_with( []( const item & it ) { return it.has_flag( flag_SMOKABLE ); } ); @@ -5661,13 +5689,13 @@ static void smoker_load_food( player &p, const tripoint &examp, comp_selection selected = p.select_item_component( comps, 1, inv, true, is_non_rotten_crafting_component ); - std::list moved = p.consume_items( selected, 1, is_non_rotten_crafting_component ); + ItemList moved = p.consume_items( selected, 1, is_non_rotten_crafting_component ); - for( const item &m : moved ) { - here.add_item( examp, m ); - p.mod_moves( -p.item_handling_cost( m ) ); + for( item *&m : moved ) { + here.add_item( examp, *m ); + p.mod_moves( -p.item_handling_cost( *m ) ); add_msg( m_info, _( "You carefully place %s %s in the rack." ), amount, - item::nname( m.typeId(), amount ) ); + item::nname( m->typeId(), amount ) ); } p.invalidate_crafting_inventory(); } @@ -5687,7 +5715,7 @@ static void mill_load_food( player &p, const tripoint &examp, inv.remove_items_with( []( const item & it ) { return it.rotten(); } ); - std::vector filtered = p.crafting_inventory().items_with( []( const item & it ) { + std::vector filtered = p.crafting_inventory().items_with( []( const item & it ) { return static_cast( it.type->milling_data ); } ); @@ -5769,12 +5797,12 @@ static void mill_load_food( player &p, const tripoint &examp, comp_selection selected = p.select_item_component( comps, 1, inv, true, is_non_rotten_crafting_component ); - std::list moved = p.consume_items( selected, 1, is_non_rotten_crafting_component ); - for( const item &m : moved ) { - here.add_item( examp, m ); - p.mod_moves( -p.item_handling_cost( m ) ); + ItemList moved = p.consume_items( selected, 1, is_non_rotten_crafting_component ); + for( item *&m : moved ) { + here.add_item( examp, *m ); + p.mod_moves( -p.item_handling_cost( *m ) ); add_msg( m_info, pgettext( "item amount and name", "You carefully place %s %s in the mill." ), - amount, item::nname( m.typeId(), amount ) ); + amount, item::nname( m->typeId(), amount ) ); } p.invalidate_crafting_inventory(); } @@ -5818,7 +5846,7 @@ void iexamine::quern_examine( player &p, const tripoint &examp ) return; } - if( items_here.size() == 1 && items_here.begin()->typeId() == itype_fake_milling_item ) { + if( items_here.size() == 1 && ( *items_here.begin() )->typeId() == itype_fake_milling_item ) { debugmsg( "f_mill_active was empty, and had fake_milling_item!" ); if( here.furn( examp ) == furn_str_id( "f_water_mill_active" ) ) { here.furn_set( examp, f_water_mill ); @@ -5833,13 +5861,13 @@ void iexamine::quern_examine( player &p, const tripoint &examp ) units::volume f_volume = 0_ml; bool f_check = false; - for( const item &it : items_here ) { - if( it.typeId() != itype_fake_milling_item ) { + for( const item * const &it : items_here ) { + if( it->typeId() != itype_fake_milling_item ) { f_check = true; - f_volume += it.volume(); + f_volume += it->volume(); } - if( active && it.typeId() == itype_fake_milling_item ) { - time_left = time_duration::from_turns( it.item_counter ); + if( active && it->typeId() == itype_fake_milling_item ) { + time_left = time_duration::from_turns( it->item_counter ); } } @@ -5894,18 +5922,18 @@ void iexamine::quern_examine( player &p, const tripoint &examp ) if( items_here.empty() ) { pop += _( "…that it is empty." ); } else { - std::map mill_list; - for( const item &it : items_here ) { - if( it.typeId() == itype_fake_milling_item ) { + std::map mill_list; + for( const item * const &it : items_here ) { + if( it->typeId() == itype_fake_milling_item ) { pop += "\n" + colorize( _( "You see that the milling process is not yet complete." ), c_red ) + "\n"; continue; } - mill_list[it] += it.count(); + mill_list[it] += it->count(); } for( auto it_mill : mill_list ) { - pop += "-> " + item::nname( it_mill.first.typeId(), - it_mill.first.count() ) + ( ( it_mill.second > 1 ) ? " (" + std::to_string( + pop += "-> " + item::nname( it_mill.first->typeId(), + it_mill.first->count() ) + ( ( it_mill.second > 1 ) ? " (" + std::to_string( it_mill.second ) + ")\n" : "\n" ); } } @@ -5927,12 +5955,12 @@ void iexamine::quern_examine( player &p, const tripoint &examp ) case 3: // remove food for( map_stack::iterator it = items_here.begin(); it != items_here.end(); ) { - if( it->typeId() != itype_fake_milling_item ) { + if( ( *it )->typeId() != itype_fake_milling_item ) { // get handling cost before the item reference is invalidated - const int handling_cost = -p.item_handling_cost( *it ); + const int handling_cost = -p.item_handling_cost( **it ); - add_msg( _( "You remove %s from the mill." ), it->tname() ); - here.add_item_or_charges( p.pos(), *it ); + add_msg( _( "You remove %s from the mill." ), ( *it )->tname() ); + here.add_item_or_charges( p.pos(), **it ); it = items_here.erase( it ); p.mod_moves( handling_cost ); } else { @@ -5980,12 +6008,13 @@ void iexamine::smoker_options( player &p, const tripoint &examp ) here.furn_set( examp, f_smoking_rack ); return; } - if( portable && items_here.size() == 1 && items_here.begin()->typeId() == itype_fake_smoke_plume ) { + if( portable && items_here.size() == 1 && + ( *items_here.begin() )->typeId() == itype_fake_smoke_plume ) { debugmsg( "f_metal_smoking_rack_active was empty, and had fake_smoke_plume!" ); here.furn_set( examp, f_metal_smoking_rack ); items_here.erase( items_here.begin() ); return; - } else if( items_here.size() == 1 && items_here.begin()->typeId() == itype_fake_smoke_plume ) { + } else if( items_here.size() == 1 && ( *items_here.begin() )->typeId() == itype_fake_smoke_plume ) { debugmsg( "f_smoking_rack_active was empty, and had fake_smoke_plume!" ); here.furn_set( examp, f_smoking_rack ); items_here.erase( items_here.begin() ); @@ -5999,13 +6028,13 @@ void iexamine::smoker_options( player &p, const tripoint &examp ) units::volume f_volume = 0_ml; bool f_check = false; - for( const item &it : items_here ) { - if( it.is_food() ) { + for( const item * const &it : items_here ) { + if( it->is_food() ) { f_check = true; - f_volume += it.volume(); + f_volume += it->volume(); } - if( active && it.typeId() == itype_fake_smoke_plume ) { - time_left = time_duration::from_turns( it.item_counter ); + if( active && it->typeId() == itype_fake_smoke_plume ) { + time_left = time_duration::from_turns( it->item_counter ); hours_left = to_hours( time_left ); minutes_left = to_minutes( time_left ) + 1; } @@ -6106,12 +6135,13 @@ void iexamine::smoker_options( player &p, const tripoint &examp ) if( items_here.empty() ) { pop += _( "…that it is empty." ); } else { - for( const item &it : items_here ) { - if( it.typeId() == itype_fake_smoke_plume ) { + for( const item * const &it : items_here ) { + if( it->typeId() == itype_fake_smoke_plume ) { pop += "\n" + colorize( _( "You see some smoldering embers there." ), c_red ) + "\n"; continue; } - pop += "-> " + item::nname( it.typeId(), it.charges ) + " (" + std::to_string( it.charges ) + ")\n"; + pop += "-> " + item::nname( it->typeId(), + it->charges ) + " (" + std::to_string( it->charges ) + ")\n"; } } popup( pop, PF_NONE ); @@ -6144,12 +6174,13 @@ void iexamine::smoker_options( player &p, const tripoint &examp ) case 5: { //remove charcoal for( map_stack::iterator it = items_here.begin(); it != items_here.end(); ) { - if( ( rem_f_opt && it->is_food() ) || ( !rem_f_opt && ( it->typeId() == itype_charcoal ) ) ) { + if( ( rem_f_opt && ( *it )->is_food() ) || ( !rem_f_opt && + ( ( *it )->typeId() == itype_charcoal ) ) ) { // get handling cost before the item reference is invalidated - const int handling_cost = -p.item_handling_cost( *it ); + const int handling_cost = -p.item_handling_cost( **it ); - add_msg( _( "You remove %s from the rack." ), it->tname() ); - here.add_item_or_charges( p.pos(), *it ); + add_msg( _( "You remove %s from the rack." ), ( *it )->tname() ); + here.add_item_or_charges( p.pos(), **it ); it = items_here.erase( it ); p.mod_moves( handling_cost ); } else { @@ -6201,7 +6232,7 @@ void iexamine::dimensional_portal( player &p, const tripoint &examp ) menu.text = _( "What to do with the portal:" ); menu.desc_enabled = true; - std::vector nukes = p.all_items_with_flag( flag_CLOSES_PORTAL ); + std::vector nukes = p.all_items_with_flag( flag_CLOSES_PORTAL ); menu.addentry_desc( 0, !nukes.empty(), 'e', _( "Close from here" ), _( "Requires a nuclear explosive" ) ); menu.addentry_desc( 1, true, 'Q', _( "Sacrifice yourself" ), @@ -6210,7 +6241,7 @@ void iexamine::dimensional_portal( player &p, const tripoint &examp ) switch( menu.ret ) { case 0: { - item_location the_nuke = game_menus::inv::titled_filter_menu( []( const item & it ) { + item* the_nuke = game_menus::inv::titled_filter_menu( []( const item & it ) { return it.has_flag( flag_CLOSES_PORTAL ); }, static_cast( p ), _( "What to use to close the portal?" ) ); if( !the_nuke ) { @@ -6219,7 +6250,8 @@ void iexamine::dimensional_portal( player &p, const tripoint &examp ) } add_msg( m_good, _( "You throw the armed %s into the portal!" ), the_nuke->tname() ); - the_nuke.remove_item(); + the_nuke->detach(); + the_nuke->destroy(); g->m.translate_radius( t_dimensional_portal, t_thconc_floor, 5, examp, true ); g->win(); break; diff --git a/src/iexamine.h b/src/iexamine.h index 8e6984cb784c..fe9ecebdae51 100644 --- a/src/iexamine.h +++ b/src/iexamine.h @@ -12,6 +12,7 @@ #include "optional.h" #include "ret_val.h" #include "type_id.h" +#include "colony.h" class item; class player; @@ -122,8 +123,8 @@ cata::optional getNearFilledGasTank( const tripoint ¢er, int &gas_ bool has_keg( const tripoint &pos ); -std::list get_harvest_items( const itype &type, int plant_count, - int seed_count, bool byproducts ); +ItemList get_harvest_items( const itype &type, int plant_count, + int seed_count, bool byproducts ); // Planting functions std::vector get_seed_entries( const std::vector &seed_inv ); diff --git a/src/init.h b/src/init.h index 348c746ab8b5..dfcc4688b9ca 100644 --- a/src/init.h +++ b/src/init.h @@ -64,7 +64,7 @@ class DynamicDataLoader * JSON data dependent upon as-yet unparsed definitions * first: JSON source location, second: source identifier */ - using deferred_json = std::list>; + using deferred_json = std::vector>; private: bool finalized = false; diff --git a/src/inventory.cpp b/src/inventory.cpp index 4bd894d56045..a34d8930e35b 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -149,11 +149,11 @@ const_invslice inventory::const_slice() const return stacks; } -const std::list &inventory::const_stack( int i ) const +const ItemList &inventory::const_stack( int i ) const { if( i < 0 || i >= static_cast( items.size() ) ) { debugmsg( "Attempted to access stack %d in an inventory (size %d)", i, items.size() ); - static const std::list nullstack{}; + static const ItemList nullstack{}; return nullstack; } @@ -176,24 +176,24 @@ inventory &inventory::operator+= ( const inventory &rhs ) } return *this; } - -inventory &inventory::operator+= ( const std::list &rhs ) +/* +inventory &inventory::operator+= ( const ItemList &rhs ) { for( const auto &rh : rhs ) { - add_item( rh, false, false ); + add_item( *rh, false, false ); } return *this; -} +}*/ -inventory &inventory::operator+= ( const std::vector &rhs ) +inventory &inventory::operator+= ( const std::vector &rhs ) { for( const auto &rh : rhs ) { - add_item( rh, true ); + add_item( *rh, true ); } return *this; } -inventory &inventory::operator+= ( const item &rhs ) +inventory &inventory::operator+= ( item &rhs ) { add_item( rhs ); return *this; @@ -202,8 +202,8 @@ inventory &inventory::operator+= ( const item &rhs ) inventory &inventory::operator+= ( const item_stack &rhs ) { for( const auto &p : rhs ) { - if( !p.made_of( LIQUID ) ) { - add_item( p, true ); + if( !p->made_of( LIQUID ) ) { + add_item( *p, true ); } } return *this; @@ -214,12 +214,12 @@ inventory inventory::operator+ ( const inventory &rhs ) return inventory( *this ) += rhs; } -inventory inventory::operator+ ( const std::list &rhs ) +inventory inventory::operator+ ( const ItemList &rhs ) { return inventory( *this ) += rhs; } -inventory inventory::operator+ ( const item &rhs ) +inventory inventory::operator+ ( item &rhs ) { return inventory( *this ) += rhs; } @@ -230,7 +230,7 @@ void inventory::unsort() items_type_cached = false; } -static bool stack_compare( const std::list &lhs, const std::list &rhs ) +static bool stack_compare( const ItemList &lhs, const ItemList &rhs ) { return lhs.front() < rhs.front(); } @@ -242,10 +242,10 @@ void inventory::clear() items_type_cached = false; } -void inventory::push_back( const std::list &newits ) +void inventory::push_back( const ItemList &newits ) { for( const auto &newit : newits ) { - add_item( newit, true ); + add_item( *newit, true ); } } @@ -283,7 +283,7 @@ char inventory::find_usable_cached_invlet( const itype_id &item_type ) return 0; } -item &inventory::add_item( item newit, bool keep_invlet, bool assign_invlet, bool should_stack ) +item &inventory::add_item( item &newit, bool keep_invlet, bool assign_invlet, bool should_stack ) { binned = false; items_type_cached = false; @@ -291,26 +291,26 @@ item &inventory::add_item( item newit, bool keep_invlet, bool assign_invlet, boo if( should_stack ) { // See if we can't stack this item. for( auto &elem : items ) { - std::list::iterator it_ref = elem.begin(); - if( it_ref->stacks_with( newit ) ) { - if( it_ref->merge_charges( newit ) ) { - return *it_ref; + item *&it = *elem.begin(); + if( it->stacks_with( newit ) ) { + if( it->merge_charges( newit ) ) { + return *it; } - if( it_ref->invlet == '\0' ) { + if( it->invlet == '\0' ) { if( !keep_invlet ) { update_invlet( newit, assign_invlet ); } update_cache_with_item( newit ); - it_ref->invlet = newit.invlet; + it->invlet = newit.invlet; } else { - newit.invlet = it_ref->invlet; + newit.invlet = it->invlet; } - elem.push_back( newit ); - return elem.back(); - } else if( keep_invlet && assign_invlet && it_ref->invlet == newit.invlet && - it_ref->invlet != '\0' ) { + elem.push_back( &newit ); + return *elem.back(); + } else if( keep_invlet && assign_invlet && it->invlet == newit.invlet && + it->invlet != '\0' ) { // If keep_invlet is true, we'll be forcing other items out of their current invlet. - assign_empty_invlet( *it_ref, g->u ); + assign_empty_invlet( *it, g->u ); } } } @@ -321,21 +321,21 @@ item &inventory::add_item( item newit, bool keep_invlet, bool assign_invlet, boo } update_cache_with_item( newit ); - items.push_back( {newit} ); - return items.back().back(); + items.push_back( {&newit} ); + return *items.back().back(); } void inventory::build_items_type_cache() { items_type_cache.clear(); for( auto &elem : items ) { - itype_id type = elem.front().typeId(); + itype_id type = elem.front()->typeId(); items_type_cache[type].push_back( &elem ); } items_type_cached = true; } -item &inventory::add_item_by_items_type_cache( item newit, bool keep_invlet, bool assign_invlet, +item &inventory::add_item_by_items_type_cache( item &newit, bool keep_invlet, bool assign_invlet, bool should_stack ) { binned = false; @@ -347,7 +347,7 @@ item &inventory::add_item_by_items_type_cache( item newit, bool keep_invlet, boo if( should_stack ) { // See if we can't stack this item. for( auto &elem : items_type_cache[type] ) { - auto it_ref = elem->begin(); + auto it_ref = *elem->begin(); if( it_ref->stacks_with( newit, false, true ) ) { if( it_ref->merge_charges( newit ) ) { return *it_ref; @@ -361,8 +361,8 @@ item &inventory::add_item_by_items_type_cache( item newit, bool keep_invlet, boo } else { newit.invlet = it_ref->invlet; } - elem->push_back( newit ); - return elem->back(); + elem->push_back( &newit ); + return *elem->back(); } else if( keep_invlet && assign_invlet && it_ref->invlet == newit.invlet && it_ref->invlet != '\0' ) { // If keep_invlet is true, we'll be forcing other items out of their current invlet. @@ -377,17 +377,17 @@ item &inventory::add_item_by_items_type_cache( item newit, bool keep_invlet, boo } update_cache_with_item( newit ); - items.push_back( {newit} ); + items.push_back( {&newit} ); items_type_cache[type].push_back( &items.back() ); - return items.back().back(); + return *items.back().back(); } -void inventory::add_item_keep_invlet( item newit ) +void inventory::add_item_keep_invlet( item &newit ) { add_item( newit, true ); } -void inventory::push_back( item newit ) +void inventory::push_back( item &newit ) { add_item( newit ); } @@ -405,24 +405,25 @@ void inventory::restack( player &p ) binned = false; items_type_cached = false; - std::list to_restack; + ItemList to_restack; int idx = 0; for( invstack::iterator iter = items.begin(); iter != items.end(); ++iter, ++idx ) { - std::list &stack = *iter; - item &topmost = stack.front(); + std::vector &stack = *iter; + item &topmost = *stack.front(); const item *invlet_item = p.invlet_to_item( topmost.invlet ); if( !inv_chars.valid( topmost.invlet ) || ( invlet_item != nullptr && position_by_item( invlet_item ) != idx ) ) { assign_empty_invlet( topmost, p ); for( auto &stack_iter : stack ) { - stack_iter.invlet = topmost.invlet; + stack_iter->invlet = topmost.invlet; } } - + //TODO!: check // remove non-matching items, stripping off end of stack so the first item keeps the invlet. - while( stack.size() > 1 && !topmost.stacks_with( stack.back() ) ) { - to_restack.splice( to_restack.begin(), *iter, --stack.end() ); + while( stack.size() > 1 && !topmost.stacks_with( *stack.back() ) ) { + //TODO!: restore next + //to_restack.splice( to_restack.begin(), *iter, --stack.end() ); } } @@ -430,11 +431,12 @@ void inventory::restack( player &p ) // separate loop to ensure that ALL stacks are homogeneous for( invstack::iterator iter = items.begin(); iter != items.end(); ++iter ) { for( invstack::iterator other = iter; other != items.end(); ++other ) { - if( iter != other && iter->front().stacks_with( other->front() ) ) { - if( other->front().count_by_charges() ) { - iter->front().charges += other->front().charges; + if( iter != other && iter->front()->stacks_with( *other->front() ) ) { + if( other->front()->count_by_charges() ) { + iter->front()->charges += other->front()->charges; } else { - iter->splice( iter->begin(), *other ); + //TODO!:restore next + //iter->splice( iter->begin(), *other ); } other = items.erase( other ); --other; @@ -444,13 +446,13 @@ void inventory::restack( player &p ) //re-add non-matching items for( auto &elem : to_restack ) { - add_item( elem ); + add_item( *elem ); } //Ensure that all items in the same stack have the same invlet. - for( std::list< item > &outer : items ) { - for( item &inner : outer ) { - inner.invlet = outer.front().invlet; + for( std::vector< item * > &outer : items ) { + for( item *&inner : outer ) { + inner->invlet = outer.front()->invlet; } } items.sort( stack_compare ); @@ -463,8 +465,8 @@ void inventory::restack( player &p ) static int count_charges_in_list( const itype *type, const map_stack &items ) { for( const auto &candidate : items ) { - if( candidate.type == type ) { - return candidate.charges; + if( candidate->type == type ) { + return candidate->charges; } } return 0; @@ -518,7 +520,8 @@ void inventory::form_from_map( map &m, std::vector pts, const Characte const std::vector tool_list = f.crafting_pseudo_item_types(); if( !tool_list.empty() ) { for( const itype &type : tool_list ) { - item furn_item( type.get_id(), calendar::turn, 0 ); + //TODO!: check + item &furn_item = *item::spawn_temporary( type.get_id(), calendar::turn, 0 ); furn_item.set_flag( "PSEUDO" ); const itype_id &ammo = furn_item.ammo_default(); if( furn_item.has_flag( "USES_GRID_POWER" ) ) { @@ -537,38 +540,40 @@ void inventory::form_from_map( map &m, std::vector pts, const Characte for( auto &i : m.i_at( p ) ) { // if it's *the* player requesting this from from map inventory // then don't allow items owned by another faction to be factored into recipe components etc. - if( pl && !i.is_owned_by( *pl, true ) ) { + if( pl && !i->is_owned_by( *pl, true ) ) { continue; } - if( allow_liquids || !i.made_of( LIQUID ) ) { - add_item_by_items_type_cache( i, false, assign_invlet ); + if( allow_liquids || !i->made_of( LIQUID ) ) { + add_item_by_items_type_cache( *i, false, assign_invlet ); } } } // Kludges for now! if( m.has_nearby_fire( p, 0 ) ) { - item fire( "fire", bday ); + //TODO!: checkkckc + item &fire = *item::spawn_temporary( "fire", bday ); fire.charges = 1; add_item_by_items_type_cache( fire ); } // Handle any water from infinite map sources. - item water = m.water_from( p ); - if( !water.is_null() ) { - add_item_by_items_type_cache( water ); + //TODO!: check + item *water = &m.water_from( p ); + if( !water->is_null() ) { + add_item_by_items_type_cache( *water ); } // kludge that can probably be done better to check specifically for toilet water to use in // crafting if( m.furn( p ).obj().examine == &iexamine::toilet ) { // get water charges at location auto toilet = m.i_at( p ); - auto water = toilet.end(); + water = nullptr; for( auto candidate = toilet.begin(); candidate != toilet.end(); ++candidate ) { - if( candidate->typeId() == itype_water ) { - water = candidate; + if( ( *candidate )->typeId() == itype_water ) { + water = *candidate; break; } } - if( water != toilet.end() && water->charges > 0 ) { + if( water != nullptr && water->charges > 0 ) { add_item_by_items_type_cache( *water ); } } @@ -598,13 +603,13 @@ void inventory::form_from_map( map &m, std::vector pts, const Characte if( cargo ) { const auto items = veh->get_items( cargo->part_index() ); for( const auto &it : items ) { - add_item_by_items_type_cache( it, false, false ); + add_item_by_items_type_cache( *it, false, false ); } } if( faupart ) { for( const auto &it : veh->fuels_left() ) { - item fuel( it.first, bday ); + item &fuel = *item::spawn_temporary( it.first, bday ); if( fuel.made_of( LIQUID ) ) { fuel.charges = it.second; add_item_by_items_type_cache( fuel ); @@ -613,77 +618,77 @@ void inventory::form_from_map( map &m, std::vector pts, const Characte } if( kpart ) { - item hotplate( "hotplate", bday ); + item &hotplate = *item::spawn_temporary( "hotplate", bday ); hotplate.charges = veh->fuel_left( itype_battery, true ); hotplate.item_tags.insert( "PSEUDO" ); // TODO: Allow disabling hotplate.item_tags.insert( "HEATS_FOOD" ); add_item_by_items_type_cache( hotplate ); - item pot( "pot", bday ); + item &pot = *item::spawn_temporary( "pot", bday ); pot.set_flag( "PSEUDO" ); add_item_by_items_type_cache( pot ); - item pan( "pan", bday ); + item &pan = *item::spawn_temporary( "pan", bday ); pan.set_flag( "PSEUDO" ); add_item_by_items_type_cache( pan ); } if( weldpart ) { - item welder( "welder", bday ); + item &welder = *item::spawn_temporary( "welder", bday ); welder.charges = veh->fuel_left( itype_battery, true ); welder.item_tags.insert( "PSEUDO" ); add_item_by_items_type_cache( welder ); - item soldering_iron( "soldering_iron", bday ); + item &soldering_iron = *item::spawn_temporary( "soldering_iron", bday ); soldering_iron.charges = veh->fuel_left( itype_battery, true ); soldering_iron.item_tags.insert( "PSEUDO" ); add_item_by_items_type_cache( soldering_iron ); } if( craftpart ) { - item vac_sealer( "vac_sealer", bday ); + item &vac_sealer = *item::spawn_temporary( "vac_sealer", bday ); vac_sealer.charges = veh->fuel_left( itype_battery, true ); vac_sealer.item_tags.insert( "PSEUDO" ); add_item_by_items_type_cache( vac_sealer ); - item dehydrator( "dehydrator", bday ); + item &dehydrator = *item::spawn_temporary( "dehydrator", bday ); dehydrator.charges = veh->fuel_left( itype_battery, true ); dehydrator.item_tags.insert( "PSEUDO" ); add_item_by_items_type_cache( dehydrator ); - item food_processor( "food_processor", bday ); + item &food_processor = *item::spawn_temporary( "food_processor", bday ); food_processor.charges = veh->fuel_left( itype_battery, true ); food_processor.item_tags.insert( "PSEUDO" ); add_item_by_items_type_cache( food_processor ); - item press( "press", bday ); + item &press = *item::spawn_temporary( "press", bday ); press.charges = veh->fuel_left( itype_battery, true ); press.set_flag( "PSEUDO" ); add_item_by_items_type_cache( press ); } if( forgepart ) { - item forge( "forge", bday ); + item &forge = *item::spawn_temporary( "forge", bday ); forge.charges = veh->fuel_left( itype_battery, true ); forge.item_tags.insert( "PSEUDO" ); add_item_by_items_type_cache( forge ); } if( kilnpart ) { - item kiln( "kiln", bday ); + item &kiln = *item::spawn_temporary( "kiln", bday ); kiln.charges = veh->fuel_left( itype_battery, true ); kiln.item_tags.insert( "PSEUDO" ); add_item_by_items_type_cache( kiln ); } if( chempart ) { - item chemistry_set( "chemistry_set", bday ); + item &chemistry_set = *item::spawn_temporary( "chemistry_set", bday ); chemistry_set.charges = veh->fuel_left( itype_battery, true ); chemistry_set.item_tags.insert( "PSEUDO" ); add_item_by_items_type_cache( chemistry_set ); - item electrolysis_kit( "electrolysis_kit", bday ); + item &electrolysis_kit = *item::spawn_temporary( "electrolysis_kit", bday ); electrolysis_kit.charges = veh->fuel_left( itype_battery, true ); electrolysis_kit.item_tags.insert( "PSEUDO" ); add_item_by_items_type_cache( electrolysis_kit ); } if( autoclavepart ) { - item autoclave( "autoclave", bday ); + item &autoclave = *item::spawn_temporary( "autoclave", bday ); autoclave.charges = veh->fuel_left( itype_battery, true ); autoclave.item_tags.insert( "PSEUDO" ); add_item_by_items_type_cache( autoclave ); @@ -692,10 +697,10 @@ void inventory::form_from_map( map &m, std::vector pts, const Characte pts.clear(); } -std::list inventory::reduce_stack( const int position, const int quantity ) +ItemList inventory::reduce_stack( const int position, const int quantity ) { int pos = 0; - std::list ret; + ItemList ret; for( invstack::iterator iter = items.begin(); iter != items.end(); ++iter ) { if( position == pos ) { binned = false; @@ -705,7 +710,8 @@ std::list inventory::reduce_stack( const int position, const int quantity items.erase( iter ); } else { for( int i = 0 ; i < quantity ; i++ ) { - ret.push_back( remove_item( &iter->front() ) ); + //TODO!: check + ret.push_back( &remove_item( iter->front() ) ); } } break; @@ -715,7 +721,7 @@ std::list inventory::reduce_stack( const int position, const int quantity return ret; } -item inventory::remove_item( const item *it ) +item &inventory::remove_item( const item *it ) { auto tmp = remove_items_with( [&it]( const item & i ) { return &i == it; @@ -723,13 +729,13 @@ item inventory::remove_item( const item *it ) if( !tmp.empty() ) { binned = false; items_type_cached = false; - return tmp.front(); + return *tmp.front(); } debugmsg( "Tried to remove a item not in inventory." ); - return item(); + return null_item_reference(); } -item inventory::remove_item( const int position ) +item &inventory::remove_item( const int position ) { int pos = 0; for( invstack::iterator iter = items.begin(); iter != items.end(); ++iter ) { @@ -737,12 +743,12 @@ item inventory::remove_item( const int position ) binned = false; items_type_cached = false; if( iter->size() > 1 ) { - std::list::iterator stack_member = iter->begin(); - char invlet = stack_member->invlet; + ItemList::iterator stack_member = iter->begin(); + char invlet = ( *stack_member )->invlet; ++stack_member; - stack_member->invlet = invlet; + ( *stack_member )->invlet = invlet; } - item ret = iter->front(); + item &ret = *iter->front(); iter->erase( iter->begin() ); if( iter->empty() ) { items.erase( iter ); @@ -752,12 +758,12 @@ item inventory::remove_item( const int position ) ++pos; } - return item(); + return null_item_reference(); } -std::list inventory::remove_randomly_by_volume( const units::volume &volume ) +ItemList inventory::remove_randomly_by_volume( const units::volume &volume ) { - std::list result; + ItemList result; units::volume volume_dropped = 0_ml; while( volume_dropped < volume ) { units::volume cumulative_volume = 0_ml; @@ -765,19 +771,20 @@ std::list inventory::remove_randomly_by_volume( const units::volume &volum auto chosen_item = chosen_stack->begin(); for( auto stack = items.begin(); stack != items.end(); ++stack ) { for( auto stack_it = stack->begin(); stack_it != stack->end(); ++stack_it ) { - cumulative_volume += stack_it->volume(); - if( x_in_y( stack_it->volume().value(), cumulative_volume.value() ) ) { + cumulative_volume += ( *stack_it )->volume(); + if( x_in_y( ( *stack_it )->volume().value(), cumulative_volume.value() ) ) { chosen_item = stack_it; chosen_stack = stack; } } } - volume_dropped += chosen_item->volume(); + volume_dropped += ( *chosen_item )->volume(); + //TODO!: check result.push_back( std::move( *chosen_item ) ); chosen_item = chosen_stack->erase( chosen_item ); if( chosen_item == chosen_stack->begin() && !chosen_stack->empty() ) { // preserve the invlet when removing the first item of a stack - chosen_item->invlet = result.back().invlet; + ( *chosen_item )->invlet = result.back()->invlet; } if( chosen_stack->empty() ) { binned = false; @@ -792,7 +799,7 @@ void inventory::dump( std::vector &dest ) { for( auto &elem : items ) { for( auto &elem_stack_iter : elem ) { - dest.push_back( &elem_stack_iter ); + dest.push_back( elem_stack_iter ); } } } @@ -806,7 +813,7 @@ const item &inventory::find_item( int position ) const for( int j = 0; j < position; ++j ) { ++iter; } - return iter->front(); + return *iter->front(); } item &inventory::find_item( int position ) @@ -818,7 +825,7 @@ int inventory::invlet_to_position( char invlet ) const { int i = 0; for( const auto &elem : items ) { - if( elem.begin()->invlet == invlet ) { + if( ( *elem.begin() )->invlet == invlet ) { return i; } ++i; @@ -831,7 +838,7 @@ int inventory::position_by_item( const item *it ) const int p = 0; for( const auto &stack : items ) { for( const auto &e : stack ) { - if( e.has_item( *it ) ) { + if( e->has_item( *it ) ) { return p; } } @@ -844,7 +851,7 @@ int inventory::position_by_type( const itype_id &type ) const { int i = 0; for( auto &elem : items ) { - if( elem.front().typeId() == type ) { + if( elem.front()->typeId() == type ) { return i; } ++i; @@ -852,16 +859,16 @@ int inventory::position_by_type( const itype_id &type ) const return INT_MIN; } -std::list inventory::use_amount( itype_id it, int quantity, - const std::function &filter ) +ItemList inventory::use_amount( itype_id it, int quantity, + const std::function &filter ) { items.sort( stack_compare ); - std::list ret; + ItemList ret; for( invstack::iterator iter = items.begin(); iter != items.end() && quantity > 0; /* noop */ ) { - for( std::list::iterator stack_iter = iter->begin(); + for( ItemList::iterator stack_iter = iter->begin(); stack_iter != iter->end() && quantity > 0; /* noop */ ) { - if( stack_iter->use_amount( it, quantity, ret, filter ) ) { + if( ( *stack_iter )->use_amount( it, quantity, ret, filter ) ) { stack_iter = iter->erase( stack_iter ); } else { ++stack_iter; @@ -902,11 +909,11 @@ int inventory::leak_level( const std::string &flag ) const for( const auto &elem : items ) { for( const auto &elem_stack_iter : elem ) { - if( elem_stack_iter.has_flag( flag ) ) { - if( elem_stack_iter.has_flag( flag_LEAK_ALWAYS ) ) { - ret += elem_stack_iter.volume() / units::legacy_volume_factor; - } else if( elem_stack_iter.has_flag( flag_LEAK_DAM ) && elem_stack_iter.damage() > 0 ) { - ret += elem_stack_iter.damage_level( 4 ); + if( elem_stack_iter->has_flag( flag ) ) { + if( elem_stack_iter->has_flag( flag_LEAK_ALWAYS ) ) { + ret += elem_stack_iter->volume() / units::legacy_volume_factor; + } else if( elem_stack_iter->has_flag( flag_LEAK_DAM ) && elem_stack_iter->damage() > 0 ) { + ret += elem_stack_iter->damage_level( 4 ); } } } @@ -918,7 +925,7 @@ int inventory::worst_item_value( npc *p ) const { int worst = 99999; for( const auto &elem : items ) { - const item &it = elem.front(); + const item &it = *elem.front(); int val = p->value( it ); if( val < worst ) { worst = val; @@ -930,7 +937,7 @@ int inventory::worst_item_value( npc *p ) const bool inventory::has_enough_painkiller( int pain ) const { for( const auto &elem : items ) { - const item &it = elem.front(); + const item &it = *elem.front(); if( ( pain <= 35 && it.typeId() == itype_aspirin ) || ( pain >= 50 && it.typeId() == itype_oxycodone ) || it.typeId() == itype_tramadol || it.typeId() == itype_codeine ) { @@ -946,7 +953,7 @@ item *inventory::most_appropriate_painkiller( int pain ) item *ret = &null_item_reference(); for( auto &elem : items ) { int diff = 9999; - itype_id type = elem.front().typeId(); + itype_id type = elem.front()->typeId(); if( type == itype_aspirin ) { diff = std::abs( pain - 15 ); } else if( type == itype_codeine ) { @@ -961,7 +968,7 @@ item *inventory::most_appropriate_painkiller( int pain ) if( diff < difference ) { difference = diff; - ret = &elem.front(); + ret = elem.front(); } } return ret; @@ -971,10 +978,10 @@ void inventory::rust_iron_items() { for( auto &elem : items ) { for( auto &elem_stack_iter : elem ) { - if( elem_stack_iter.made_of( material_id( "iron" ) ) && - !elem_stack_iter.has_flag( flag_WATERPROOF_GUN ) && - !elem_stack_iter.has_flag( flag_WATERPROOF ) && - elem_stack_iter.damage() < elem_stack_iter.max_damage() / 2 && + if( elem_stack_iter->made_of( material_id( "iron" ) ) && + !elem_stack_iter->has_flag( flag_WATERPROOF_GUN ) && + !elem_stack_iter->has_flag( flag_WATERPROOF ) && + elem_stack_iter->damage() < elem_stack_iter->max_damage() / 2 && //Passivation layer prevents further rusting one_in( 500 ) && //Scale with volume, bigger = slower (see #24204) @@ -983,12 +990,12 @@ void inventory::rust_iron_items() 14 * std::cbrt( 0.5 * std::max( 0.05, static_cast( - elem_stack_iter.base_volume().value() ) / 250 ) ) ) ) && + elem_stack_iter->base_volume().value() ) / 250 ) ) ) ) && // ^season length ^14/5*0.75/pi (from volume of sphere) //Freshwater without oxygen rusts slower than air g->m.water_from( g->u.pos() ).typeId() == itype_salt_water ) { - elem_stack_iter.inc_damage( DT_ACID ); // rusting never completely destroys an item - add_msg( m_bad, _( "Your %s is damaged by rust." ), elem_stack_iter.tname() ); + elem_stack_iter->inc_damage( DT_ACID ); // rusting never completely destroys an item + add_msg( m_bad, _( "Your %s is damaged by rust." ), elem_stack_iter->tname() ); } } } @@ -999,7 +1006,7 @@ units::mass inventory::weight() const units::mass ret = 0_gram; for( const auto &elem : items ) { for( const auto &elem_stack_iter : elem ) { - ret += elem_stack_iter.weight(); + ret += elem_stack_iter->weight(); } } return ret; @@ -1009,7 +1016,7 @@ units::mass inventory::weight() const // of items given template void for_each_item_in_both( - const invstack &items, const std::map &other, const F &f ) + const invstack &items, const std::map &other, const F &f ) { // Shortcut the logic in the common case where other is empty if( other.empty() ) { @@ -1017,7 +1024,7 @@ void for_each_item_in_both( } for( const auto &elem : items ) { - const item &representative = elem.front(); + item &representative = *elem.front(); auto other_it = other.find( &representative ); if( other_it == other.end() ) { continue; @@ -1025,12 +1032,13 @@ void for_each_item_in_both( int num_to_count = other_it->second; if( representative.count_by_charges() ) { - item copy = representative; + //TODO!: what the shit is this used for + item © = *item::spawn_temporary( representative ); copy.charges = std::min( copy.charges, num_to_count ); f( copy ); } else { for( const auto &elem_stack_iter : elem ) { - f( elem_stack_iter ); + f( *elem_stack_iter ); if( --num_to_count <= 0 ) { break; } @@ -1062,7 +1070,7 @@ units::volume inventory::volume() const units::volume ret = 0_ml; for( const auto &elem : items ) { for( const auto &elem_stack_iter : elem ) { - ret += elem_stack_iter.volume(); + ret += elem_stack_iter->volume(); } } return ret; @@ -1089,10 +1097,10 @@ units::volume inventory::volume_without( const excluded_stacks &without ) const std::vector inventory::active_items() { std::vector ret; - for( std::list &elem : items ) { - for( item &elem_stack_iter : elem ) { - if( elem_stack_iter.needs_processing() ) { - ret.push_back( &elem_stack_iter ); + for( ItemList &elem : items ) { + for( item * const &elem_stack_iter : elem ) { + if( elem_stack_iter->needs_processing() ) { + ret.push_back( elem_stack_iter ); } } } @@ -1102,10 +1110,10 @@ std::vector inventory::active_items() enchantment inventory::get_active_enchantment_cache( const Character &owner ) const { enchantment temp_cache; - for( const std::list &elem : items ) { - for( const item &check_item : elem ) { - for( const enchantment &ench : check_item.get_enchantments() ) { - if( ench.is_active( owner, check_item ) ) { + for( const ItemList &elem : items ) { + for( const item * const &check_item : elem ) { + for( const enchantment &ench : check_item->get_enchantments() ) { + if( ench.is_active( owner, *check_item ) ) { temp_cache.force_add( ench ); } } @@ -1192,7 +1200,7 @@ void inventory::assign_empty_invlet( item &it, const Character &p, const bool fo } // No free hotkey exist, re-use some of the existing ones for( auto &elem : items ) { - item &o = elem.front(); + item &o = *elem.front(); if( o.invlet != 0 ) { it.invlet = o.invlet; o.invlet = 0; @@ -1254,7 +1262,7 @@ void inventory::update_invlet( item &newit, bool assign_invlet ) void inventory::set_stack_favorite( const int position, const bool favorite ) { for( auto &e : *std::next( items.begin(), position ) ) { - e.set_favorite( favorite ); + e->set_favorite( favorite ); } } @@ -1263,7 +1271,7 @@ invlets_bitset inventory::allocated_invlets() const invlets_bitset invlets; for( const auto &stack : items ) { - const char invlet = stack.front().invlet; + const char invlet = stack.front()->invlet; invlets.set( invlet ); } invlets[0] = false; diff --git a/src/inventory.h b/src/inventory.h index 7b018c09305c..4806463e570c 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -28,15 +28,15 @@ class npc; class player; struct tripoint; -using invstack = std::list >; -using invslice = std::vector *>; -using const_invslice = std::vector *>; -using indexed_invslice = std::vector< std::pair*, int> >; +using invstack = std::list; +using invslice = std::vector; +using const_invslice = std::vector; +using indexed_invslice = std::vector< std::pair >; using itype_bin = std::unordered_map< itype_id, std::list >; using invlets_bitset = std::bitset::max()>; /** First element is pointer to item stack (first item), second is amount. */ -using excluded_stacks = std::map; +using excluded_stacks = std::map; /** * Wrapper to handled a set of valid "inventory" letters. "inventory" can be any set of @@ -93,7 +93,7 @@ class inventory : public visitable invslice slice(); const_invslice const_slice() const; - const std::list &const_stack( int i ) const; + const ItemList &const_stack( int i ) const; size_t size() const; std::map assigned_invlet; @@ -105,25 +105,26 @@ class inventory : public visitable inventory &operator=( const inventory & ) = default; inventory &operator+= ( const inventory &rhs ); - inventory &operator+= ( const item &rhs ); - inventory &operator+= ( const std::list &rhs ); - inventory &operator+= ( const std::vector &rhs ); + inventory &operator+= ( item &rhs ); + // inventory &operator+= ( const ItemList &rhs ); + inventory &operator+= ( const std::vector &rhs ); inventory &operator+= ( const item_stack &rhs ); inventory operator+ ( const inventory &rhs ); - inventory operator+ ( const item &rhs ); - inventory operator+ ( const std::list &rhs ); + inventory operator+ ( item &rhs ); + inventory operator+ ( const ItemList &rhs ); void unsort(); // flags the inventory as unsorted void clear(); - void push_back( const std::list &newits ); + void push_back( const ItemList &newits ); // returns a reference to the added item - item &add_item( item newit, bool keep_invlet = false, bool assign_invlet = true, + item &add_item( item &newit, bool keep_invlet = false, bool assign_invlet = true, bool should_stack = true ); // use item type cache to speed up, remember to run build_items_type_cache() before using it - item &add_item_by_items_type_cache( item newit, bool keep_invlet = false, bool assign_invlet = true, + item &add_item_by_items_type_cache( item &newit, bool keep_invlet = false, + bool assign_invlet = true, bool should_stack = true ); - void add_item_keep_invlet( item newit ); - void push_back( item newit ); + void add_item_keep_invlet( item &newit ); + void push_back( item &newit ); /* Check all items for proper stacking, rearranging as needed * game pointer is not necessary, but if supplied, will ensure no overlap with @@ -147,13 +148,13 @@ class inventory : public visitable * in this inventory. * @return A copy of the removed item. */ - item remove_item( const item *it ); - item remove_item( int position ); + item &remove_item( const item *it ); + item &remove_item( int position ); /** * Randomly select items until the volume quota is filled. */ - std::list remove_randomly_by_volume( const units::volume &volume ); - std::list reduce_stack( int position, int quantity ); + ItemList remove_randomly_by_volume( const units::volume &volume ); + ItemList reduce_stack( int position, int quantity ); const item &find_item( int position ) const; item &find_item( int position ); @@ -174,8 +175,8 @@ class inventory : public visitable // Below, "amount" refers to quantity // "charges" refers to charges - std::list use_amount( itype_id it, int quantity, - const std::function &filter = return_true ); + ItemList use_amount( itype_id it, int quantity, + const std::function &filter = return_true ); bool has_tools( const itype_id &it, int quantity, const std::function &filter = return_true ) const; @@ -248,7 +249,7 @@ class inventory : public visitable char find_usable_cached_invlet( const itype_id &item_type ); invstack items; - std::map*>> items_type_cache; + std::map> items_type_cache; std::map> quality_cache; bool items_type_cached = false; diff --git a/src/inventory_ui.cpp b/src/inventory_ui.cpp index 843763d8f866..d14dd52fbf16 100644 --- a/src/inventory_ui.cpp +++ b/src/inventory_ui.cpp @@ -95,7 +95,7 @@ class selection_column_preset : public inventory_selector_preset std::string get_caption( const inventory_entry &entry ) const override { std::string res; const size_t available_count = entry.get_available_count(); - const item_location &item = entry.any_item(); + const item* item = entry.any_item(); if( entry.chosen_count > 0 && entry.chosen_count < available_count ) { //~ %1$d: chosen count, %2$d: available count @@ -120,7 +120,7 @@ class selection_column_preset : public inventory_selector_preset nc_color get_color( const inventory_entry &entry ) const override { if( entry.is_item() ) { - if( &*entry.any_item() == &g->u.weapon ) { + if( entry.any_item() == &g->u.get_weapon() ) { return c_light_blue; } else if( g->u.is_worn( *entry.any_item() ) ) { return c_cyan; @@ -135,7 +135,7 @@ static const selection_column_preset selection_preset{}; int inventory_entry::get_total_charges() const { int result = 0; - for( const item_location &location : locations ) { + for( const item* location : locations ) { result += location->charges; } return result; @@ -146,7 +146,7 @@ int inventory_entry::get_selected_charges() const assert( chosen_count <= locations.size() ); int result = 0; for( size_t i = 0; i < chosen_count; ++i ) { - const item_location &location = locations[i]; + const item*location = locations[i]; result += location->charges; } return result; @@ -310,7 +310,7 @@ bool inventory_selector_preset::is_stub_cell( const inventory_entry &entry, } void inventory_selector_preset::append_cell( const - std::function &func, + std::function &func, const std::string &title, const std::string &stub ) { // Don't capture by reference here. The func should be able to die earlier than the object itself @@ -638,12 +638,12 @@ std::vector inventory_column::get_entries( return res; } -void inventory_column::set_stack_favorite( const item_location &location, bool favorite ) +void inventory_column::set_stack_favorite( const item*location, bool favorite ) { - const item *selected_item = location.get_item(); + const item *selected_item = location; std::list to_favorite; - if( location.where() == item_location::type::character ) { + if( location->where() == item::item_location_type::character ) { int position = g->u.get_item_position( selected_item ); if( position < 0 ) { @@ -651,27 +651,27 @@ void inventory_column::set_stack_favorite( const item_location &location, bool f } else { g->u.inv.set_stack_favorite( position, !selected_item->is_favorite ); // in inventory } - } else if( location.where() == item_location::type::map ) { - auto items = g->m.i_at( location.position() ); + } else if( location->where() == item::item_location_type::map ) { + auto items = g->m.i_at( location->position() ); for( auto &item : items ) { - if( item.stacks_with( *selected_item ) ) { - to_favorite.push_back( &item ); + if( item->stacks_with( *selected_item ) ) { + to_favorite.push_back( item ); } } for( auto &item : to_favorite ) { item->set_favorite( favorite ); } - } else if( location.where() == item_location::type::vehicle ) { + } else if( location->where() == item::item_location_type::vehicle ) { const cata::optional vp = g->m.veh_at( - location.position() ).part_with_feature( "CARGO", true ); + location->position() ).part_with_feature( "CARGO", true ); assert( vp ); auto items = vp->vehicle().get_items( vp->part_index() ); for( auto &item : items ) { - if( item.stacks_with( *selected_item ) ) { - to_favorite.push_back( &item ); + if( item->stacks_with( *selected_item ) ) { + to_favorite.push_back( item ); } } for( auto *item : to_favorite ) { @@ -700,7 +700,7 @@ void inventory_column::on_input( const inventory_input &input ) select( entries.size() - 1, scroll_direction::BACKWARD ); } else if( input.action == "TOGGLE_FAVORITE" ) { if( !get_selected().locations.empty() ) { - const item_location &loc = get_selected().any_item(); + const item* loc = get_selected().any_item(); set_stack_favorite( loc, !loc->is_favorite ); } } @@ -819,7 +819,7 @@ void inventory_column::clear() paging_is_valid = false; } -bool inventory_column::select( const item_location &loc ) +bool inventory_column::select( const item*loc ) { for( size_t index = 0; index < entries.size(); ++index ) { if( entries[index].is_selectable() && entries[index].any_item() == loc ) { @@ -1052,27 +1052,29 @@ void selection_column::on_change( const inventory_entry &entry ) } } +//TODO!: restore/delete if it ends up being the same as the one below +/* // TODO: Move it into some 'item_stack' class. -static std::vector> restack_items( const std::list::const_iterator &from, - const std::list::const_iterator &to, bool check_components = false ) +static std::vector> restack_items( const ItemList::const_iterator &from, + const ItemList::const_iterator &to, bool check_components = false ) { std::vector> res; for( auto it = from; it != to; ++it ) { auto match = std::find_if( res.begin(), res.end(), [ &it, check_components ]( const std::list &e ) { - return it->display_stacked_with( *const_cast( e.back() ), check_components ); + return ( *it )->display_stacked_with( *const_cast( e.back() ), check_components ); } ); if( match != res.end() ) { - match->push_back( const_cast( &*it ) ); + match->push_back( const_cast( *it ) ); } else { - res.emplace_back( 1, const_cast( &*it ) ); + res.emplace_back( 1, const_cast( *it ) ); } } return res; -} +}*/ // TODO: Move it into some 'item_stack' class. static std::vector> restack_items( const item_stack::const_iterator &from, @@ -1083,13 +1085,13 @@ static std::vector> restack_items( const item_stack::const_ite for( auto it = from; it != to; ++it ) { auto match = std::find_if( res.begin(), res.end(), [ &it, check_components ]( const std::list &e ) { - return it->display_stacked_with( *const_cast( e.back() ), check_components ); + return ( *it )->display_stacked_with( *const_cast( e.back() ), check_components ); } ); if( match != res.end() ) { - match->push_back( const_cast( &*it ) ); + match->push_back( const_cast( *it ) ); } else { - res.emplace_back( 1, const_cast( &*it ) ); + res.emplace_back( 1, const_cast( *it ) ); } } @@ -1137,7 +1139,7 @@ const item_category *inventory_selector::naturalize_category( const item_categor } void inventory_selector::add_entry( inventory_column &target_column, - std::vector &&locations, + std::vector &&locations, const item_category *custom_category ) { if( !preset.is_shown( locations.front() ) ) { @@ -1157,30 +1159,30 @@ void inventory_selector::add_entry( inventory_column &target_column, } void inventory_selector::add_item( inventory_column &target_column, - item_location &&location, + item*location, const item_category *custom_category ) { add_entry( target_column, - std::vector( 1, location ), + std::vector( 1, location ), custom_category ); } void inventory_selector::add_items( inventory_column &target_column, - const std::function &locator, + const std::function &locator, const std::vector> &stacks, const item_category *custom_category ) { const item_category *nat_category = nullptr; for( const auto &elem : stacks ) { - std::vector locations; + std::vector locations; std::transform( elem.begin(), elem.end(), std::back_inserter( locations ), locator ); - item_location const &loc = locations.front(); + item* const &loc = locations.front(); if( custom_category == nullptr ) { nat_category = &loc->get_category(); } else if( nat_category == nullptr && preset.is_shown( loc ) ) { - nat_category = naturalize_category( *custom_category, loc.position() ); + nat_category = naturalize_category( *custom_category, loc->position() ); } add_entry( target_column, std::move( locations ), nat_category ); @@ -1189,20 +1191,20 @@ void inventory_selector::add_items( inventory_column &target_column, void inventory_selector::add_character_items( Character &character ) { - character.visit_items( [ this, &character ]( item * it ) { - if( it == &character.weapon ) { - add_item( own_gear_column, item_location( character, it ), + character.visit_items( [ this, &character]( item * it ) { + if( it == &character.get_weapon() ) { + add_item( own_gear_column, it, &item_category_id( "WEAPON_HELD" ).obj() ); } else if( character.is_worn( *it ) ) { - add_item( own_gear_column, item_location( character, it ), + add_item( own_gear_column, it, &item_category_id( "ITEMS_WORN" ).obj() ); } return VisitResponse::NEXT; } ); // Visitable interface does not support stacks so it has to be here for( const auto &elem : character.inv.slice() ) { - add_items( own_inv_column, [&character]( item * it ) { - return item_location( character, it ); + add_items( own_inv_column, []( item * it ) { + return it; }, restack_items( ( *elem ).begin(), ( *elem ).end(), preset.get_checking_components() ) ); } } @@ -1214,8 +1216,8 @@ void inventory_selector::add_map_items( const tripoint &target ) const std::string name = to_upper_case( g->m.name( target ) ); const item_category map_cat( name, no_translation( name ), 100 ); - add_items( map_column, [ &target ]( item * it ) { - return item_location( target, it ); + add_items( map_column, []( item * it ) { + return it; }, restack_items( items.begin(), items.end(), preset.get_checking_components() ), &map_cat ); } } @@ -1234,8 +1236,8 @@ void inventory_selector::add_vehicle_items( const tripoint &target ) const auto check_components = this->preset.get_checking_components(); - add_items( map_column, [ veh, part ]( item * it ) { - return item_location( vehicle_cursor( *veh, part ), it ); + add_items( map_column, []( item * it ) { + return it; }, restack_items( items.begin(), items.end(), check_components ), &vehicle_cat ); } @@ -1264,7 +1266,7 @@ void inventory_selector::clear_items() map_column.clear(); } -bool inventory_selector::select( const item_location &loc ) +bool inventory_selector::select( const item *loc ) { bool res = false; @@ -1901,7 +1903,7 @@ std::vector inventory_selector::all_bound_keys() const return retv; } -item_location inventory_pick_selector::execute() +item* inventory_pick_selector::execute() { shared_ptr_fast ui = create_or_get_ui_adaptor(); while( true ) { @@ -1915,7 +1917,7 @@ item_location inventory_pick_selector::execute() } return input.entry->any_item(); } else if( input.action == "QUIT" ) { - return item_location(); + return nullptr; } else if( input.action == "CONFIRM" ) { const inventory_entry &selected = get_active_column().get_selected(); if( selected ) { @@ -1928,7 +1930,7 @@ item_location inventory_pick_selector::execute() } if( input.action == "TOGGLE_FAVORITE" ) { - return item_location(); + return nullptr; } } } @@ -2115,10 +2117,10 @@ void inventory_iuse_selector::set_chosen_count( inventory_entry &entry, size_t c entry.get_available_count() ); to_use[it].clear(); if( entry.locations.size() == 1 ) { - to_use[it].push_back( iuse_location{ entry.locations[0], static_cast( entry.chosen_count ) } ); + to_use[it].push_back( iuse_location{ *entry.locations[0], static_cast( entry.chosen_count ) } ); } else { for( size_t i = 0; i < entry.chosen_count; i++ ) { - to_use[it].push_back( iuse_location{ entry.locations[i], 1 } ); + to_use[it].push_back( iuse_location{ *entry.locations[i], 1 } ); } } } @@ -2187,7 +2189,7 @@ drop_locations inventory_drop_selector::execute() for( const std::pair &drop_pair : dropping ) { for( auto &col : get_all_columns() ) { for( const auto &entry : col->get_entries( always_yes ) ) { - if( entry->any_item().get_item() == drop_pair.first ) { + if( entry->any_item() == drop_pair.first ) { selected_entries.push_back( std::pair( entry, drop_pair.second ) ); } } @@ -2287,11 +2289,10 @@ drop_locations inventory_drop_selector::execute() drop_locations dropped_pos_and_qty; - for( const std::pair &drop_pair : dropping ) { - item_location loc( u, const_cast( drop_pair.first ) ); + for( const std::pair &drop_pair : dropping ) { // Note: drop_location here contains location of first item in stack, // and amount of items to be dropped from the stack. - dropped_pos_and_qty.emplace_back( loc, drop_pair.second ); + dropped_pos_and_qty.emplace_back( *drop_pair.first, drop_pair.second ); } return dropped_pos_and_qty; @@ -2299,7 +2300,7 @@ drop_locations inventory_drop_selector::execute() void inventory_drop_selector::set_chosen_count( inventory_entry &entry, size_t count ) { - const item *it = entry.item_stack_on_character(); + item *it = entry.item_stack_on_character(); if( count == 0 ) { entry.chosen_count = 0; diff --git a/src/inventory_ui.h b/src/inventory_ui.h index a41c034c295d..006ca941576b 100644 --- a/src/inventory_ui.h +++ b/src/inventory_ui.h @@ -19,7 +19,6 @@ #include "cursesdef.h" #include "input.h" #include "item_handling_util.h" -#include "item_location.h" #include "memory_fast.h" #include "pimpl.h" #include "units.h" @@ -32,8 +31,8 @@ class string_input_popup; struct tripoint; class ui_adaptor; -using excluded_stack = std::pair; -using excluded_stacks = std::map; +using excluded_stack = std::pair; +using excluded_stacks = std::map; enum class navigation_mode : int { ITEM = 0, @@ -51,7 +50,7 @@ struct inventory_input; class inventory_entry { public: - std::vector locations; + std::vector locations; size_t chosen_count = 0; int custom_invlet = INT_MIN; @@ -70,7 +69,7 @@ class inventory_entry this->custom_category = custom_category; } - inventory_entry( const std::vector &locations, + inventory_entry( const std::vector &locations, const item_category *custom_category = nullptr, bool enabled = true ) : locations( locations ), @@ -105,15 +104,15 @@ class inventory_entry return is_item() && enabled; } - const item_location &any_item() const { + item* any_item() const { assert( !locations.empty() ); return locations.front(); } /** Pointer to first item in relevant stack on character. */ - const item *item_stack_on_character() const { + item *item_stack_on_character() const { assert( !locations.empty() ); - return locations.front().get_item(); + return locations.front(); } size_t get_stack_size() const { @@ -142,7 +141,7 @@ class inventory_selector_preset virtual ~inventory_selector_preset() = default; /** Does this entry satisfy the basic preset conditions? */ - virtual bool is_shown( const item_location & ) const { + virtual bool is_shown( const item * ) const { return true; } @@ -150,7 +149,7 @@ class inventory_selector_preset * The reason why this entry cannot be selected. * @return Either the reason of denial or empty string if it's accepted. */ - virtual std::string get_denial( const item_location & ) const { + virtual std::string get_denial( const item* ) const { return std::string(); } /** Whether the first item is considered to go before the second. */ @@ -184,7 +183,7 @@ class inventory_selector_preset * @param title Title of the cell. * @param stub The cell won't be "revealed" if it contains only this value */ - void append_cell( const std::function &func, + void append_cell( const std::function &func, const std::string &title = std::string(), const std::string &stub = std::string() ); void append_cell( const std::function &func, @@ -276,10 +275,10 @@ class inventory_column void add_entry( const inventory_entry &entry ); void move_entries_to( inventory_column &dest ); void clear(); - void set_stack_favorite( const item_location &location, bool favorite ); + void set_stack_favorite( const item *location, bool favorite ); /** Selects the specified location. */ - bool select( const item_location &loc ); + bool select( const item *loc ); /** * Change the selection. @@ -488,15 +487,15 @@ class inventory_selector const tripoint &pos ); void add_entry( inventory_column &target_column, - std::vector &&locations, + std::vector &&locations, const item_category *custom_category = nullptr ); void add_item( inventory_column &target_column, - item_location &&location, + item* location, const item_category *custom_category = nullptr ); void add_items( inventory_column &target_column, - const std::function &locator, + const std::function &locator, const std::vector> &stacks, const item_category *custom_category = nullptr ); @@ -561,7 +560,7 @@ class inventory_selector * @param loc Location to select * @return true on success. */ - bool select( const item_location &loc ); + bool select( const item *loc ); const inventory_entry &get_selected() { return get_active_column().get_selected(); @@ -658,7 +657,7 @@ class inventory_pick_selector : public inventory_selector const inventory_selector_preset &preset = default_preset ) : inventory_selector( p, preset ) {} - item_location execute(); + item* execute(); }; class inventory_multiselector : public inventory_selector diff --git a/src/item.cpp b/src/item.cpp index a510cb9e27b5..dcd25d2b3db3 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -282,6 +282,9 @@ static const activity_id ACT_PICKUP( "ACT_PICKUP" ); static const matec_id rapid_strike( "RAPID" ); +template<> +std::vector cata_arena::pending_deletion = {}; + class npc_class; using npc_class_id = string_id; @@ -385,7 +388,8 @@ item::item( const itype *type, time_point turn, int qty ) : type( type ), bday( } if( has_flag( flag_NANOFAB_TEMPLATE ) ) { - itype_id nanofab_recipe = item_group::item_from( item_group_id( "nanofab_recipes" ) ).typeId(); + //TODO!: ahahaha no + itype_id nanofab_recipe = item_group::item_from( item_group_id( "nanofab_recipes" ) )->typeId(); set_var( "NANOFAB_ITEM_ID", nanofab_recipe.str() ); } @@ -396,12 +400,14 @@ item::item( const itype *type, time_point turn, int qty ) : type( type ), bday( put_in( it ); } for( const itype_id &mod : type->gun->default_mods ) { - put_in( item( mod, turn, qty ) ); + //TODO!:check + put_in( *item::spawn( mod, turn, qty ) ); } } else if( type->magazine ) { if( type->magazine->count > 0 ) { - put_in( item( type->magazine->default_ammo, calendar::turn, type->magazine->count ) ); + //TODO!:check + put_in( *item::spawn( type->magazine->default_ammo, calendar::turn, type->magazine->count ) ); } } else if( goes_bad() ) { @@ -443,25 +449,20 @@ item::item( const itype *type, time_point turn, solitary_tag ) item::item( const itype_id &id, time_point turn, solitary_tag tag ) : item( & * id, turn, tag ) {} -safe_reference item::get_safe_reference() -{ - return anchor.reference_to( this ); -} - static const item *get_most_rotten_component( const item &craft ) { const item *most_rotten = nullptr; - for( const item &it : craft.components ) { - if( it.goes_bad() ) { - if( !most_rotten || it.get_relative_rot() > most_rotten->get_relative_rot() ) { - most_rotten = ⁢ + for( const item * const &it : craft.components ) { + if( it->goes_bad() ) { + if( !most_rotten || it->get_relative_rot() > most_rotten->get_relative_rot() ) { + most_rotten = it; } } } return most_rotten; } -item::item( const recipe *rec, int qty, std::list items, std::vector selections ) +item::item( const recipe *rec, int qty, ItemList items, std::vector selections ) : item( "craft", calendar::turn, qty ) { craft_data_ = cata::make_value(); @@ -480,13 +481,13 @@ item::item( const recipe *rec, int qty, std::list items, std::vectoritem_tags ) { if( json_flag::get( f ).craft_inherit() ) { set_flag( f ); } } - for( const std::string &f : component.type->get_flags() ) { + for( const std::string &f : component->type->get_flags() ) { if( json_flag::get( f ).craft_inherit() ) { set_flag( f ); } @@ -500,12 +501,13 @@ item::item( const recipe *rec, int qty, std::list items, std::vectorcorpse = &mt.obj(); - if( result.corpse->has_flag( MF_REVIVES ) ) { + if( result->corpse->has_flag( MF_REVIVES ) ) { if( one_in( 20 ) ) { - result.set_flag( "REVIVE_SPECIAL" ); + result->set_flag( "REVIVE_SPECIAL" ); } - result.set_var( "upgrade_time", std::to_string( upgrade_time ) ); + result->set_var( "upgrade_time", std::to_string( upgrade_time ) ); } // This is unconditional because the const itemructor above sets result.name to // "human corpse". - result.corpse_name = name; + result->corpse_name = name; - return result; + return *result; } item &item::convert( const itype_id &new_type ) @@ -669,7 +672,8 @@ item &item::ammo_set( const itype_id &ammo, int qty ) } } } - put_in( item( mag ) ); + //TODO!: check + put_in( *item::spawn( mag ) ); } magazine_current()->ammo_set( ammo, qty ); } @@ -718,15 +722,15 @@ item &item::set_damage( int qty ) return *this; } -item item::split( int qty ) +item &item::split( int qty ) { if( !count_by_charges() || qty <= 0 || qty >= charges ) { - return item(); + return null_item_reference(); } - item res = *this; - res.charges = qty; + item *res = item::spawn( *this ); + res->charges = qty; charges -= qty; - return res; + return *res; } bool item::is_null() const @@ -837,25 +841,25 @@ bool item::is_worn_only_with( const item &it ) const return( ( has_flag( flag_POWERARMOR_EXTERNAL ) || has_flag( flag_POWERARMOR_MOD ) ) && it.has_flag( flag_POWERARMOR_EXO ) ); } - -item item::in_its_container() const +//TODO!: update location stuffs +item &item::in_its_container() { return in_container( type->default_container.value_or( "null" ) ); } -item item::in_container( const itype_id &cont ) const +item &item::in_container( const itype_id &cont ) { if( !cont.is_null() ) { - item ret( cont, birthday() ); - ret.put_in( *this ); - if( made_of( LIQUID ) && ret.is_container() ) { + item *ret = item::spawn( cont, birthday() ); + ret->put_in( *this ); + if( made_of( LIQUID ) && ret->is_container() ) { // Note: we can't use any of the normal container functions as they check the // container being suitable (seals, watertight etc.) - ret.contents.back().charges = charges_per_volume( ret.get_container_capacity() ); + ret->contents.back().charges = charges_per_volume( ret->get_container_capacity() ); } - ret.invlet = invlet; - return ret; + ret->invlet = invlet; + return *ret; } else { return *this; } @@ -987,7 +991,7 @@ bool item::merge_charges( const item &rhs ) return true; } -void item::put_in( const item &payload ) +void item::put_in( item &payload ) { contents.insert_item( payload ); } @@ -3102,8 +3106,8 @@ void item::component_info( std::vector &info, const iteminfo_query *pa } else if( get_var( "bionics_scanned_by", -1 ) == get_avatar().getID().get_value() ) { // TODO: Extract into a more proper place (function in namespace) std::string bionics_string = enumerate_as_string( components.begin(), components.end(), - []( const item & entry ) -> std::string { - return entry.is_bionic() ? entry.display_name() : ""; + []( const item * const & entry ) -> std::string { + return entry->is_bionic() ? entry->display_name() : ""; }, enumeration_conjunction::none ); info.push_back( iteminfo( "DESCRIPTION", string_format( _( "Contains: %s" ), bionics_string ) ) ); @@ -4240,9 +4244,9 @@ void item::on_wear( Character &p ) for( auto &elem : p.worn ) { for( std::pair< body_part, int > &mod_part : mod_parts ) { bpid = convert_bp( mod_part.first ); - if( elem.get_covered_body_parts().test( mod_part.first ) && - elem.has_flag( flag_POWERARMOR_MOD ) ) { - if( elem.is_sided() && elem.get_side() == bpid->part_side ) { + if( elem->get_covered_body_parts().test( mod_part.first ) && + elem->has_flag( flag_POWERARMOR_MOD ) ) { + if( elem->is_sided() && elem->get_side() == bpid->part_side ) { mod_part.second++; continue; } @@ -4291,7 +4295,7 @@ void item::on_wear( Character &p ) } std::string transform_flag = actor->dependencies; for( const auto &elem : p.worn ) { - if( elem.has_flag( transform_flag ) && elem.active != active ) { + if( elem->has_flag( transform_flag ) && elem->active != active ) { transform = true; } } @@ -4884,8 +4888,8 @@ units::mass item::weight( bool include_contents, bool integral ) const if( is_craft() ) { units::mass ret = 0_gram; - for( const item &it : components ) { - ret += it.weight(); + for( const item * const &it : components ) { + ret += it->weight(); } return ret; } @@ -5001,8 +5005,8 @@ units::volume item::base_volume() const if( is_craft() ) { units::volume ret = 0_ml; - for( const item &it : components ) { - ret += it.base_volume(); + for( const item * const &it : components ) { + ret += it->base_volume(); } return ret; } @@ -5030,8 +5034,8 @@ units::volume item::volume( bool integral ) const if( is_craft() ) { units::volume ret = 0_ml; - for( const item &it : components ) { - ret += it.volume(); + for( const item * const &it : components ) { + ret += it->volume(); } return ret; } @@ -5263,8 +5267,8 @@ item &item::unset_flag( const std::string &flag ) item &item::set_flag_recursive( const std::string &flag ) { set_flag( flag ); - for( item &comp : components ) { - comp.set_flag_recursive( flag ); + for( item * const &comp : components ) { + comp->set_flag_recursive( flag ); } return *this; } @@ -5682,8 +5686,8 @@ int item::get_encumber( const Character &p ) const if( t != nullptr && t->max_encumber != 0 ) { units::volume char_storage( 0_ml ); - for( const item &e : p.worn ) { - char_storage += e.get_storage(); + for( const item * const &e : p.worn ) { + char_storage += e->get_storage(); } if( char_storage != 0_ml ) { @@ -7840,7 +7844,7 @@ const use_function *item::get_use_internal( const std::string &use_name ) const return nullptr; } -item *item::get_usable_item( const std::string &use_name ) +item *item::get_usable_item( const std::string &use_name ) const { item *ret = nullptr; visit_items( @@ -7894,9 +7898,9 @@ item::reload_option::reload_option( const reload_option & ) = default; item::reload_option &item::reload_option::operator=( const reload_option & ) = default; -item::reload_option::reload_option( const player *who, const item *target, const item *parent, - const item_location &ammo ) : - who( who ), target( target ), ammo( ammo ), parent( parent ) +item::reload_option::reload_option( const player *who, item *target, const item *parent, + item &ammo ) : + who( who ), target( target ), ammo( &ammo ), parent( parent ) { if( this->target->is_ammo_belt() && this->target->type->magazine->linkage ) { max_qty = this->who->charges_of( * this->target->type->magazine->linkage ); @@ -7906,7 +7910,7 @@ item::reload_option::reload_option( const player *who, const item *target, const int item::reload_option::moves() const { - int mv = ammo.obtain_cost( *who, qty() ) + who->item_reload_cost( *target, *ammo, qty() ); + int mv = ammo->obtain_cost( *who, qty() ) + who->item_reload_cost( *target, *ammo, qty() ); if( parent != target ) { if( parent->is_gun() ) { mv += parent->get_reload_time(); @@ -7977,13 +7981,13 @@ void item::casings_handle( const std::function &func ) contents.casings_handle( func ); } -bool item::reload( player &u, item_location loc, int qty ) +bool item::reload( player &u, item& loc, int qty ) { if( qty <= 0 ) { debugmsg( "Tried to reload zero or less charges" ); return false; } - item *ammo = loc.get_item(); + item *ammo = &loc; if( ammo == nullptr || ammo->is_null() ) { debugmsg( "Tried to reload using non-existent ammo" ); return false; @@ -8054,7 +8058,8 @@ bool item::reload( player &u, item_location loc, int qty ) // eject magazine to player inventory and try to dispose of it from there item &mag = u.i_add( *magazine_current() ); - if( !u.dispose_item( item_location( u, &mag ), prompt ) ) { + if( !u.dispose_item( mag, prompt ) ) { + //TODO!: check this, no more clone u.remove_item( mag ); // user canceled so delete the clone return false; } @@ -8062,7 +8067,8 @@ bool item::reload( player &u, item_location loc, int qty ) } put_in( *ammo ); - loc.remove_item(); + //TODO!: check this, might not be correct here + loc.detach(); return true; } else { @@ -8091,7 +8097,8 @@ bool item::reload( player &u, item_location loc, int qty ) container->remove_item( container->contents.front() ); u.inv.restack( u ); // emptied containers do not stack with non-empty ones } else { - loc.remove_item(); + //TODO!: check + loc.detach(); } } return true; @@ -8345,7 +8352,7 @@ int item::get_remaining_capacity_for_liquid( const item &liquid, bool allow_buck int item::get_remaining_capacity_for_liquid( const item &liquid, const Character &p, std::string *err ) const { - const bool allow_bucket = this == &p.weapon || !p.has_item( *this ); + const bool allow_bucket = (this == &p.get_weapon()) || !p.has_item( *this ); int res = get_remaining_capacity_for_liquid( liquid, allow_bucket, err ); if( res > 0 && !type->rigid && p.inv.has_item( *this ) ) { @@ -8362,7 +8369,7 @@ int item::get_remaining_capacity_for_liquid( const item &liquid, const Character return res; } -bool item::use_amount( const itype_id &it, int &quantity, std::list &used, +bool item::use_amount( const itype_id &it, int &quantity, ItemList &used, const std::function &filter ) { // Remember quantity so that we can unseal self @@ -8392,11 +8399,11 @@ bool item::use_amount( const itype_id &it, int &quantity, std::list &used, return use_amount_internal( it, quantity, used, filter ); } -bool item::use_amount_internal( const itype_id &it, int &quantity, std::list &used, +bool item::use_amount_internal( const itype_id &it, int &quantity, ItemList &used, const std::function &filter ) { if( typeId() == it && quantity > 0 && filter( *this ) ) { - used.push_back( *this ); + used.push_back( this ); quantity--; return true; } else { @@ -8482,7 +8489,7 @@ void item::set_countdown( int num_turns ) charges = num_turns; } -bool item::use_charges( const itype_id &what, int &qty, std::list &used, +bool item::use_charges( const itype_id &what, int &qty, ItemList &used, const tripoint &pos, const std::function &filter ) { std::vector del; @@ -8502,7 +8509,8 @@ bool item::use_charges( const itype_id &what, int &qty, std::list &used, int n = std::min( e->ammo_remaining(), qty ); qty -= n; - used.push_back( item( *e ).ammo_set( e->ammo_current(), n ) ); + //TODO!: restore next + //used.push_back( item( *e ).ammo_set( e->ammo_current(), n ) ); e->ammo_consume( n, pos ); } return VisitResponse::SKIP; @@ -8511,18 +8519,18 @@ bool item::use_charges( const itype_id &what, int &qty, std::list &used, if( e->typeId() == what ) { // if can supply excess charges split required off leaving remainder in-situ - item obj = e->split( qty ); + item &obj = e->split( qty ); if( parent ) { parent->on_contents_changed(); } if( !obj.is_null() ) { - used.push_back( obj ); + used.push_back( &obj ); qty = 0; return VisitResponse::ABORT; } qty -= e->charges; - used.push_back( *e ); + used.push_back( e ); del.push_back( e ); } // items counted by charges are not themselves expected to be containers @@ -8620,10 +8628,10 @@ bool item::will_explode_in_fire() const return false; } -bool item::detonate( const tripoint &p, std::vector &drops ) +bool item::detonate( const tripoint &p, std::vector &drops ) { if( type->explosion ) { - explosion_handler::explosion( p, type->explosion, activated_by.get() ); + explosion_handler::explosion( p, type->explosion, activated_by ); return true; } else if( type->ammo && ( type->ammo->special_cookoff || type->ammo->cookoff ) ) { int charges_remaining = charges; @@ -8634,12 +8642,12 @@ bool item::detonate( const tripoint &p, std::vector &drops ) if( ammo_type.special_cookoff ) { // If it has a special effect just trigger it. - apply_ammo_effects( p, ammo_type.ammo_effects, activated_by.get() ); + apply_ammo_effects( p, ammo_type.ammo_effects, activated_by ); } charges_remaining -= rounds_exploded; if( charges_remaining > 0 ) { - item temp_item = *this; - temp_item.charges = charges_remaining; + item *temp_item = item::spawn( *this ); + temp_item->charges = charges_remaining; drops.push_back( temp_item ); } @@ -8647,7 +8655,7 @@ bool item::detonate( const tripoint &p, std::vector &drops ) } else if( !contents.empty() && ( !type->magazine || !type->magazine->protects_contents ) ) { std::vector removed_items; bool detonated = false; - for( item *it : contents.all_items_top() ) { + for( item *&it : contents.all_items_top() ) { if( it->detonate( p, drops ) ) { removed_items.push_back( it ); detonated = true; @@ -8765,9 +8773,9 @@ std::string item::components_to_string() const { using t_count_map = std::map; t_count_map counts; - for( const item &elem : components ) { - if( !elem.has_flag( flag_BYPRODUCT ) ) { - const std::string name = elem.display_name(); + for( const item * const &elem : components ) { + if( !elem->has_flag( flag_BYPRODUCT ) ) { + const std::string name = elem->display_name(); counts[name]++; } } @@ -8787,8 +8795,8 @@ uint64_t item::make_component_hash() const { // First we need to sort the IDs so that identical ingredients give identical hashes. std::multiset id_set; - for( const item &it : components ) { - id_set.insert( it.typeId().str() ); + for( const item * const &it : components ) { + id_set.insert( it->typeId().str() ); } std::string concatenated_ids; @@ -8947,7 +8955,6 @@ void item::process_artifact( player *carrier, const tripoint & /*pos*/ ) } } - std::vector item::mutations_from_wearing( const Character &guy ) const { if( !is_relic() ) { @@ -9175,7 +9182,7 @@ bool item::process_extinguish( player *carrier, const tripoint &pos ) extinguish = true; } if( !extinguish || - ( in_inv && precipitation && carrier->weapon.has_flag( flag_RAIN_PROTECT ) ) ) { + ( in_inv && precipitation && carrier->get_weapon().has_flag( flag_RAIN_PROTECT ) ) ) { return false; //nothing happens } if( carrier != nullptr ) { @@ -9355,7 +9362,7 @@ bool item::process_tool( player *carrier, const tripoint &pos ) bool active = false; std::string transform_flag = actor->dependencies; for( const auto &elem : carrier->worn ) { - if( elem.active && elem.has_flag( transform_flag ) ) { + if( elem->active && elem->has_flag( transform_flag ) ) { active = true; break; } @@ -9419,18 +9426,18 @@ bool item::process_tool( player *carrier, const tripoint &pos ) } std::string transformed_flag = actor->flag; for( auto &elem : carrier->worn ) { - if( elem.active && elem.has_flag( transformed_flag ) ) { - if( !elem.type->can_use( "set_transformed" ) ) { + if( elem->active && elem->has_flag( transformed_flag ) ) { + if( !elem->type->can_use( "set_transformed" ) ) { debugmsg( "Expected set_transformed function" ); return false; } const set_transformed_iuse *actor = dynamic_cast - ( elem.get_use( "set_transformed" )->get_actor_ptr() ); + ( elem->get_use( "set_transformed" )->get_actor_ptr() ); if( actor == nullptr ) { debugmsg( "iuse_actor type descriptor and actual type mismatch" ); return false; } - actor->bypass( *carrier, elem, false, pos ); + actor->bypass( *carrier, *elem, false, pos ); } } } @@ -9640,7 +9647,7 @@ bool item::has_effect_when_carried( art_effect_passive effect ) const bool item::is_seed() const { - return !!type->seed; + return type->is_seed(); } time_duration item::get_plant_epoch() const @@ -9736,11 +9743,11 @@ std::string item::type_name( unsigned int quantity ) const // Apply conditional names, in order. for( const conditional_name &cname : type->conditional_names ) { // Lambda for recursively searching for a item ID among all components. - std::function )> component_id_contains = - [&]( std::list components ) { - for( const item &component : components ) { - if( component.typeId().str().find( cname.condition ) != std::string::npos || - component_id_contains( component.components ) ) { + std::function )> component_id_contains = + [&]( std::vector components ) { + for( const item *component : components ) { + if( component->typeId().str().find( cname.condition ) != std::string::npos || + component_id_contains( component->components ) ) { return true; } } @@ -9933,15 +9940,15 @@ std::vector item::get_uncraft_components() const } } else { //Make a new vector of components from the registered components - for( const item &component : components ) { + for( const item * const &component : components ) { auto iter = std::find_if( ret.begin(), ret.end(), [component]( item_comp & obj ) { - return obj.type == component.typeId(); + return obj.type == component->typeId(); } ); if( iter != ret.end() ) { - iter->count += component.count(); + iter->count += component->count(); } else { - ret.push_back( item_comp( component.typeId(), component.count() ) ); + ret.push_back( item_comp( component->typeId(), component->count() ) ); } } } diff --git a/src/item.h b/src/item.h index d87bc0be29be..a655d0a7b1e7 100644 --- a/src/item.h +++ b/src/item.h @@ -14,13 +14,14 @@ #include #include "calendar.h" +#include "cata_arena.h" #include "enums.h" #include "flat_set.h" #include "gun_mode.h" #include "io_tags.h" #include "item_contents.h" -#include "item_location.h" #include "optional.h" +#include "locations.h" #include "pimpl.h" #include "safe_reference.h" #include "string_id.h" @@ -173,11 +174,22 @@ inline iteminfo::flags &operator|=( iteminfo::flags &l, iteminfo::flags r ) inline bool is_crafting_component( const item &component ); +template<> +std::vector cata_arena::pending_deletion; + +/** + * Returns a reference to a null item (see @ref item::is_null). The reference is always valid + * and stays valid until the program ends. + */ +item &null_item_reference(); + + class item : public visitable { public: - using FlagsSetType = cata::flat_set; - + struct default_charges_tag {}; + struct solitary_tag {}; + private: item(); item( item && ); @@ -189,17 +201,17 @@ class item : public visitable explicit item( const itype *type, time_point turn = calendar::turn, int qty = -1 ); /** Suppress randomization and always start with default quantity of charges */ - struct default_charges_tag {}; + item( const itype_id &id, time_point turn, default_charges_tag ); item( const itype *type, time_point turn, default_charges_tag ); /** Default (or randomized) charges except if counted by charges then only one charge */ - struct solitary_tag {}; + item( const itype_id &id, time_point turn, solitary_tag ); item( const itype *type, time_point turn, solitary_tag ); /** For constructing in-progress crafts */ - item( const recipe *rec, int qty, std::list items, std::vector selections ); + item( const recipe *rec, int qty, std::vector items, std::vector selections ); // Legacy constructor for constructing from string rather than itype_id // TODO: remove this and migrate code using it. @@ -208,12 +220,132 @@ class item : public visitable item( itype_id( itype ), std::forward( args )... ) {} + loc_item_location *location; + + public: + + enum class item_location_type : int { + invalid = 0, + character = 1, + map = 2, + vehicle = 3, + container = 4 + }; + + using FlagsSetType = cata::flat_set; + friend cata_arena; + friend item &null_item_reference(); + ~item(); - /** Return a pointer-like type that's automatically invalidated if this - * item is destroyed or assigned-to */ - safe_reference get_safe_reference(); + inline static item *spawn( JsonIn &jsin ) { + item *p = spawn(); + p->deserialize( jsin ); + return p; + } + + inline static item *spawn( const item &source ) { + if( source.is_null() ) { + return &null_item_reference(); + } + item *p = static_cast( std::allocator_traits>::allocate( arena, 1 ) ); + //std::allocator_traits>::construct( arena, p, args... ); + ::new( static_cast( p ) ) item( source ); + return p; + } + + template + inline static item *spawn( T... args ) { + item *p = static_cast( std::allocator_traits>::allocate( arena, 1 ) ); + //std::allocator_traits>::construct( arena, p, args... ); + ::new( static_cast( p ) ) item( std::forward( args )... ); + return p; + } + + inline static item *spawn_temporary( const item &source ) { + if( source.is_null() ) { + return &null_item_reference(); + } + item *p = item::spawn( source ); + p->destroy(); + p->set_location( new template_item_location() ); + return p; + } + + template + inline static item *spawn_temporary( T... args ) { + item *p = item::spawn( std::forward( args )... ); + p->destroy(); + p->set_location( new template_item_location() ); + return p; + } + + + void destroy() { + if( is_null() ) { + return; + } + if( location != nullptr ) { + location->release_for_destroy(); + } + cata_arena::mark_for_destruction( this ); + } + + void detach() { + if( is_null() ) { + return; + } + if( location == nullptr ) { + //debugmsg( "Attempted to remove the location of an item that doesn't have one." ); + return; + } + location->release(); + delete location; + location = nullptr; + } + + void set_location( loc_item_location *own ) { + if( is_null() ) { + return; + } + if( location != nullptr ) { + //TODO!:move these to cpp and restore + //debugmsg( "Attempted to set the location of an item that already has one." ); + detach(); + } + location = own; + } + + void move_location( loc_item_location *own ) { + if( is_null() ) { + return; + } + detach(); + location = own; + } + + void check_location() { + if( location == nullptr ) { + //TODO!: report this as an error + } + } + bool is_loaded() { + if( is_null() ) { + return false; + } + if( location == nullptr ) { + return false; + } + return location->is_loaded(); + } + + void remove_location(){ + delete location; + location=nullptr; + } + + //TODO!: check these "filters" are being used right and at least update the comments, they're not filters anymore /** * Filter converting this instance to another type preserving all other aspects * @param new_type the type id to convert to @@ -272,7 +404,7 @@ class item : public visitable * @param qty number of required charges to split from source * @return new instance containing exactly qty charges or null item if splitting failed */ - item split( int qty ); + item &split( int qty ); /** * Make a corpse of the given monster type. @@ -289,8 +421,8 @@ class item : public visitable * With the default parameters it makes a human corpse, created at the current turn. */ /*@{*/ - static item make_corpse( const mtype_id &mt = string_id::NULL_ID(), - time_point turn = calendar::turn, const std::string &name = "", int upgrade_time = -1 ); + static item &make_corpse( const mtype_id &mt = string_id::NULL_ID(), + time_point turn = calendar::turn, const std::string &name = "", int upgrade_time = -1 ); /*@}*/ /** * @return The monster type associated with this item (@ref corpse). It is usually the @@ -476,12 +608,11 @@ class item : public visitable reload_option( const reload_option & ); reload_option &operator=( const reload_option & ); - reload_option( const player *who, const item *target, const item *parent, - const item_location &ammo ); + reload_option( const player *who, item *target, const item *parent, item &ammo ); const player *who = nullptr; - const item *target = nullptr; - item_location ammo; + item *target = nullptr; + item *ammo; int qty() const { return qty_; @@ -506,7 +637,7 @@ class item : public visitable * @param loc Location of ammo to be reloaded * @param qty caps reloading to this (or fewer) units */ - bool reload( player &u, item_location loc, int qty ); + bool reload( player &u, item &loc, int qty ); template void io( Archive & ); @@ -646,7 +777,7 @@ class item : public visitable * @param filter Must return true for use to occur. * @return true if this item should be deleted (count-by-charges items with no remaining charges) */ - bool use_charges( const itype_id &what, int &qty, std::list &used, const tripoint &pos, + bool use_charges( const itype_id &what, int &qty, ItemList &used, const tripoint &pos, const std::function &filter = return_true ); /** @@ -675,7 +806,7 @@ class item : public visitable * @param used On success all consumed items will be stored here. * @param filter Must return true for use to occur. */ - bool use_amount( const itype_id &it, int &quantity, std::list &used, + bool use_amount( const itype_id &it, int &quantity, ItemList &used, const std::function &filter = return_true ); /** Permits filthy components, should only be used as a helper in creating filters */ @@ -735,14 +866,14 @@ class item : public visitable /** * Puts the given item into this one, no checks are performed. */ - void put_in( const item &payload ); + void put_in( item &payload ); /** * Returns this item into its default container. If it does not have a default container, * returns this. It's intended to be used like \code newitem = newitem.in_its_container();\endcode */ - item in_its_container() const; - item in_container( const itype_id &container_type ) const; + item &in_its_container(); + item &in_container( const itype_id &container_type ); /*@}*/ bool item_has_uses_recursive() const; @@ -880,7 +1011,7 @@ class item : public visitable * potentially destroying other items and invalidating iterators. * Should NOT be called on an item on the map, but on a local copy. */ - bool detonate( const tripoint &p, std::vector &drops ); + bool detonate( const tripoint &p, std::vector &drops ); bool will_explode_in_fire() const; @@ -1952,7 +2083,7 @@ class item : public visitable * use_function with type use_name. Returns the first item that does have * such type or nullptr if none found. */ - item *get_usable_item( const std::string &use_name ); + item *get_usable_item( const std::string &use_name ) const; /** * How many units (ammo or charges) are remaining? @@ -2087,7 +2218,7 @@ class item : public visitable * * @param parents Items to inherit from */ - void inherit_flags( const std::list &parents, const recipe &making ); + void inherit_flags( const ItemList &parents, const recipe &making ); void set_tools_to_continue( bool value ); bool has_tools_to_continue() const; @@ -2109,8 +2240,30 @@ class item : public visitable double bonus_from_enchantments_wielded( double base, enchant_vals::mod value, bool round = false ) const; + /** Returns the type of location where the item is found */ + item_location_type where() const; + + /** Returns the position where the item is found */ + tripoint position() const; + + /** Describes the item location + * @param ch if set description is relative to character location */ + std::string describe_location( const Character *ch = nullptr ) const; + + /** Move an item from the location to the character inventory + * @param ch Character who's inventory gets the item + * @param qty if specified limits maximum obtained charges */ + void obtain( Character &ch, int qty = -1 ); + + /** Calculate (but do not deduct) number of moves required to obtain an item + * @see item::obtain */ + int obtain_cost( const Character &ch, int qty = -1 ) const; + + /** returns the parent item, or a null pointer if it has no parent */ + item* parent_item() const; + private: - bool use_amount_internal( const itype_id &it, int &quantity, std::list &used, + bool use_amount_internal( const itype_id &it, int &quantity, ItemList &used, const std::function &filter = return_true ); const use_function *get_use_internal( const std::string &use_name ) const; bool process_internal( player *carrier, const tripoint &pos, bool activate, float insulation = 1, @@ -2156,14 +2309,13 @@ class item : public visitable const itype *type; item_contents contents; - std::list components; + ItemList components; /** What faults (if any) currently apply to this item */ std::set faults; // TODO: Move to private ASAP FlagsSetType item_tags; // generic item specific flags private: - safe_reference_anchor anchor; const itype *curammo = nullptr; std::map item_vars; const mtype *corpse = nullptr; @@ -2229,10 +2381,12 @@ class item : public visitable int damage_ = 0; light_emission light = nolight; + static cata_arena arena; + public: char invlet = 0; // Inventory letter bool active = false; // If true, it has active effects to be processed - safe_reference activated_by; + player* activated_by; bool is_favorite = false; void set_favorite( bool favorite ); @@ -2250,12 +2404,6 @@ class item : public visitable bool item_compare_by_charges( const item &left, const item &right ); bool item_ptr_compare_by_charges( const item *left, const item *right ); -/** - * Returns a reference to a null item (see @ref item::is_null). The reference is always valid - * and stays valid until the program ends. - */ -item &null_item_reference(); - /** * Default filter for crafting component searches */ diff --git a/src/item_action.cpp b/src/item_action.cpp index 772e39de5911..20c490bd98e3 100644 --- a/src/item_action.cpp +++ b/src/item_action.cpp @@ -88,8 +88,8 @@ bool item::item_has_uses_recursive() const bool item_contents::item_has_uses_recursive() const { - for( const item &it : items ) { - if( it.item_has_uses_recursive() ) { + for( const item * const &it : items ) { + if( it->item_has_uses_recursive() ) { return true; } } @@ -235,13 +235,14 @@ void game::item_action_menu() // HACK: A bit of a hack for now. If more pseudos get implemented, this should be un-hacked std::vector pseudos; - item toolset( "toolset", calendar::turn ); if( u.has_active_bionic( bio_tools ) ) { - pseudos.push_back( &toolset ); + item *toolset = item::spawn_temporary( "toolset", calendar::turn ); + pseudos.push_back( toolset ); } - item bio_claws_item( static_cast( bio_claws_weapon ), calendar::turn ); if( u.has_active_bionic( bio_claws ) ) { - pseudos.push_back( &bio_claws_item ); + item *bio_claws_item = item::spawn_temporary( static_cast( bio_claws_weapon ), + calendar::turn ); + pseudos.push_back( bio_claws_item ); } item_action_map iactions = gen.map_actions_to_items( u, pseudos ); diff --git a/src/item_contents.cpp b/src/item_contents.cpp index 62ff02b5dbdd..137af00f50aa 100644 --- a/src/item_contents.cpp +++ b/src/item_contents.cpp @@ -18,9 +18,9 @@ bool item_contents::empty() const return items.empty(); } -ret_val item_contents::insert_item( const item &it ) +ret_val item_contents::insert_item( item &it ) { - items.push_back( it ); + items.push_back( &it ); return ret_val::make_success(); } @@ -31,8 +31,8 @@ size_t item_contents::num_item_stacks() const bool item_contents::spill_contents( const tripoint &pos ) { - for( item &it : items ) { - get_map().add_item_or_charges( pos, it ); + for( item * const &it : items ) { + get_map().add_item_or_charges( pos, *it ); } items.clear(); @@ -42,14 +42,15 @@ bool item_contents::spill_contents( const tripoint &pos ) void item_contents::handle_liquid_or_spill( Character &guy ) { for( auto iter = items.begin(); iter != items.end(); ) { - if( iter->made_of( LIQUID ) ) { - item liquid( *iter ); + if( ( *iter )->made_of( LIQUID ) ) { + + item *liquid = *iter; iter = items.erase( iter ); - liquid_handler::handle_all_liquid( liquid, 1 ); + liquid_handler::handle_all_liquid( *liquid, 1 ); } else { - item i_copy( *iter ); + item *i_copy = *iter; iter = items.erase( iter ); - guy.i_add_or_drop( i_copy ); + guy.i_add_or_drop( *i_copy ); } } } @@ -58,14 +59,14 @@ void item_contents::casings_handle( const std::function &func ) { for( auto it = items.begin(); it != items.end(); ) { - if( it->has_flag( "CASING" ) ) { - it->unset_flag( "CASING" ); - if( func( *it ) ) { + if( ( *it )->has_flag( "CASING" ) ) { + ( *it )->unset_flag( "CASING" ); + if( func( **it ) ) { it = items.erase( it ); continue; } // didn't handle the casing so reset the flag ready for next call - it->set_flag( "CASING" ); + ( *it )->set_flag( "CASING" ); } ++it; } @@ -79,14 +80,14 @@ void item_contents::clear_items() void item_contents::set_item_defaults() { /* For Items with a magazine or battery in its contents */ - for( item &contained_item : items ) { + for( item * const &contained_item : items ) { /* for guns and other items defined to have a magazine but don't use "ammo" */ - if( contained_item.is_magazine() ) { - contained_item.ammo_set( - contained_item.ammo_default(), contained_item.ammo_capacity() / 2 + if( contained_item->is_magazine() ) { + contained_item->ammo_set( + contained_item->ammo_default(), contained_item->ammo_capacity() / 2 ); } else { //Contents are batteries or food - contained_item.charges = contained_item.typeId()->charges_default(); + contained_item->charges = contained_item->typeId()->charges_default(); } } } @@ -94,72 +95,90 @@ void item_contents::set_item_defaults() void item_contents::migrate_item( item &obj, const std::set &migrations ) { for( const itype_id &c : migrations ) { - if( std::none_of( items.begin(), items.end(), [&]( const item & e ) { - return e.typeId() == c; + if( std::none_of( items.begin(), items.end(), [&]( const item * const & e ) { + return e->typeId() == c; } ) ) { - obj.put_in( item( c, obj.birthday() ) ); + obj.put_in( *item::spawn( c, obj.birthday() ) ); } } } bool item_contents::has_any_with( const std::function &filter ) const { - return std::any_of( items.begin(), items.end(), filter ); + return std::any_of( items.begin(), items.end(), [&filter]( const item * const & it ) -> bool{ return filter( *it );} ); } bool item_contents::stacks_with( const item_contents &rhs ) const { - return std::equal( items.begin(), items.end(), rhs.items.begin(), []( const item & a, - const item & b ) { - return a.charges == b.charges && a.stacks_with( b ); + return std::equal( items.begin(), items.end(), rhs.items.begin(), []( const item * const & a, + const item * const & b ) { + return a->charges == b->charges && a->stacks_with( *b ); } ); } item *item_contents::get_item_with( const std::function &filter ) { - auto bomb_it = std::find_if( items.begin(), items.end(), filter ); + auto bomb_it = std::find_if( items.begin(), + items.end(), [&filter]( const item * const & it ) -> bool{ return filter( *it );} ); if( bomb_it == items.end() ) { return nullptr; } else { - return &*bomb_it; + return *bomb_it; } } -std::list item_contents::all_items_top() +std::vector item_contents::all_items_top() { - std::list ret; - for( item &it : items ) { - ret.push_back( &it ); + std::vector ret; + for( item * const &it : items ) { + ret.push_back( it ); } return ret; } -std::list item_contents::all_items_top() const +std::vector item_contents::all_items_top() const { - std::list ret; - for( const item &it : items ) { - ret.push_back( &it ); + std::vector ret; + for( const item * const &it : items ) { + ret.push_back( it ); } return ret; } -std::list item_contents::all_items_ptr() +void item_contents::remove_top( item *it ) +{ + std::vector::iterator iter = std::find_if( items.begin(), + items.end(), [&it]( item *&against ) { + return against == it; + } ); + it->remove_location();//TODO!: need to make all the other functions here do locations too + items.erase( iter ); +} + +std::vector::iterator item_contents::remove_top( std::vector::iterator &it ) +{ + ( *it )->remove_location(); + return items.erase( it ); +} + +std::vector item_contents::all_items_ptr() { - std::list ret; - for( item &it : items ) { - ret.push_back( &it ); - std::list inside = it.contents.all_items_ptr(); + std::vector ret; + for( item * const &it : items ) { + ret.push_back( it ); + std::vector inside = it->contents.all_items_ptr(); + //TODO!:check ret.insert( ret.end(), inside.begin(), inside.end() ); } return ret; } -std::list item_contents::all_items_ptr() const +std::vector item_contents::all_items_ptr() const { - std::list ret; - for( const item &it : items ) { - ret.push_back( &it ); - std::list inside = it.contents.all_items_ptr(); + std::vector ret; + for( const item * const &it : items ) { + ret.push_back( it ); + std::vector inside = it->contents.all_items_ptr(); ret.insert( ret.end(), inside.begin(), inside.end() ); } return ret; @@ -168,9 +187,9 @@ std::list item_contents::all_items_ptr() const std::vector item_contents::gunmods() { std::vector res; - for( item &e : items ) { - if( e.is_gunmod() ) { - res.push_back( &e ); + for( item *&e : items ) { + if( e->is_gunmod() ) { + res.push_back( e ); } } return res; @@ -179,9 +198,9 @@ std::vector item_contents::gunmods() std::vector item_contents::gunmods() const { std::vector res; - for( const item &e : items ) { - if( e.is_gunmod() ) { - res.push_back( &e ); + for( const item * const &e : items ) { + if( e->is_gunmod() ) { + res.push_back( e ); } } return res; @@ -189,29 +208,29 @@ std::vector item_contents::gunmods() const item &item_contents::front() { - return items.front(); + return *items.front(); } const item &item_contents::front() const { - return items.front(); + return *items.front(); } item &item_contents::back() { - return items.back(); + return *items.back(); } const item &item_contents::back() const { - return items.back(); + return *items.back(); } units::volume item_contents::item_size_modifier() const { units::volume ret = 0_ml; - for( const item &it : items ) { - ret += it.volume(); + for( const item * const &it : items ) { + ret += it->volume(); } return ret; } @@ -219,8 +238,8 @@ units::volume item_contents::item_size_modifier() const units::mass item_contents::item_weight_modifier() const { units::mass ret = 0_gram; - for( const item &it : items ) { - ret += it.weight(); + for( const item * const &it : items ) { + ret += it->weight(); } return ret; } @@ -228,8 +247,8 @@ units::mass item_contents::item_weight_modifier() const int item_contents::best_quality( const quality_id &id ) const { int ret = INT_MIN; - for( const item &it : items ) { - ret = std::max( ret, it.get_quality( id ) ); + for( const item * const &it : items ) { + ret = std::max( ret, it->get_quality( id ) ); } return ret; } diff --git a/src/item_contents.h b/src/item_contents.h index 84e7f7723c74..98d1eb7148b9 100644 --- a/src/item_contents.h +++ b/src/item_contents.h @@ -25,19 +25,23 @@ class item_contents public: item_contents() = default; /** used to aid migration */ - item_contents( const std::list &items ) : items( items ) {} + item_contents( const std::vector &items ) : items( items ) {} bool empty() const; /** returns a list of pointers to all top-level items */ - std::list all_items_top(); + std::vector all_items_top(); /** returns a list of pointers to all top-level items */ - std::list all_items_top() const; + std::vector all_items_top() const; + + /** removes a top-level item */ + void remove_top( item *it ); + std::vector::iterator remove_top( std::vector::iterator &it ); // returns a list of pointers to all items inside recursively - std::list all_items_ptr(); + std::vector all_items_ptr(); // returns a list of pointers to all items inside recursively - std::list all_items_ptr() const; + std::vector all_items_ptr() const; /** gets all gunmods in the item */ std::vector gunmods(); @@ -64,7 +68,7 @@ class item_contents int best_quality( const quality_id &id ) const; - ret_val insert_item( const item &it ); + ret_val insert_item( item &it ); /** * returns the number of items stacks in contents @@ -98,12 +102,12 @@ class item_contents VisitResponse visit_contents( const std::function &func, item *parent = nullptr ); bool remove_internal( const std::function &filter, - int &count, std::list &res ); + int &count, std::list &res ); void serialize( JsonOut &json ) const; void deserialize( JsonIn &jsin ); private: - std::list items; + ItemList items; }; #endif // CATA_SRC_ITEM_CONTENTS_H diff --git a/src/item_factory.cpp b/src/item_factory.cpp index 4cf42f174ddc..7fba1557e8c5 100644 --- a/src/item_factory.cpp +++ b/src/item_factory.cpp @@ -3234,7 +3234,7 @@ void item_group::debug_spawn() for( size_t a = 0; a < 100; a++ ) { const auto items = items_from( groups[index], calendar::turn ); for( auto &it : items ) { - itemnames[it.display_name()]++; + itemnames[it->display_name()]++; } } // Invert the map to get sorting! diff --git a/src/item_factory.h b/src/item_factory.h index 036264abd0ac..5f1dc9ed3d7d 100644 --- a/src/item_factory.h +++ b/src/item_factory.h @@ -28,7 +28,7 @@ template class value_ptr; bool item_is_blacklisted( const itype_id &id ); -using Item_list = std::vector; +using Item_list = std::vector; class Item_factory; class JsonArray; diff --git a/src/item_group.cpp b/src/item_group.cpp index 418439ca8cdf..2dda0890c79f 100644 --- a/src/item_group.cpp +++ b/src/item_group.cpp @@ -34,7 +34,7 @@ Item_spawn_data::ItemList Item_spawn_data::create( const time_point &birthday ) return create( birthday, rec ); } -item Item_spawn_data::create_single( const time_point &birthday ) const +item *Item_spawn_data::create_single( const time_point &birthday ) const { RecursionList rec; return create_single( birthday, rec ); @@ -47,39 +47,39 @@ Single_item_creator::Single_item_creator( const std::string &_id, Type _type, in { } -item Single_item_creator::create_single( const time_point &birthday, RecursionList &rec ) const +item *Single_item_creator::create_single( const time_point &birthday, RecursionList &rec ) const { - item tmp; + item *tmp; if( type == S_ITEM ) { if( id == "corpse" ) { - tmp = item::make_corpse( mtype_id::NULL_ID(), birthday ); + tmp = &item::make_corpse( mtype_id::NULL_ID(), birthday ); } else { - tmp = item( id, birthday ); + tmp = item::spawn( id, birthday ); } } else if( type == S_ITEM_GROUP ) { if( std::find( rec.begin(), rec.end(), id ) != rec.end() ) { debugmsg( "recursion in item spawn list %s", id.c_str() ); - return item( itype_id::NULL_ID(), birthday ); + return nullptr; } rec.push_back( id ); Item_spawn_data *isd = item_controller->get_group( item_group_id( id ) ); if( isd == nullptr ) { debugmsg( "unknown item spawn list %s", id.c_str() ); - return item( itype_id::NULL_ID(), birthday ); + return nullptr; } tmp = isd->create_single( birthday, rec ); rec.erase( rec.end() - 1 ); } else if( type == S_NONE ) { - return item( itype_id::NULL_ID(), birthday ); + return nullptr; } - if( one_in( 3 ) && tmp.has_flag( flag_VARSIZE ) ) { - tmp.set_flag( "FIT" ); + if( one_in( 3 ) && tmp->has_flag( flag_VARSIZE ) ) { + tmp->set_flag( "FIT" ); } if( modifier ) { - modifier->modify( tmp ); + tmp = modifier->modify( tmp ); } else { // TODO: change the spawn lists to contain proper references to containers - tmp = tmp.in_its_container(); + tmp = &tmp->in_its_container(); } return tmp; } @@ -97,7 +97,7 @@ Item_spawn_data::ItemList Single_item_creator::create( const time_point &birthda for( ; cnt > 0; cnt-- ) { if( type == S_ITEM ) { const auto itm = create_single( birthday, rec ); - if( !itm.is_null() ) { + if( itm != nullptr ) { result.push_back( itm ); } } else { @@ -115,7 +115,7 @@ Item_spawn_data::ItemList Single_item_creator::create( const time_point &birthda rec.erase( rec.end() - 1 ); if( modifier ) { for( auto &elem : tmplist ) { - modifier->modify( elem ); + elem = modifier->modify( elem ); } } result.insert( result.end(), tmplist.begin(), tmplist.end() ); @@ -236,50 +236,51 @@ Item_modifier::Item_modifier() { } -void Item_modifier::modify( item &new_item ) const +item *Item_modifier::modify( item *new_item ) const { - if( new_item.is_null() ) { - return; + if( new_item->is_null() ) { + return new_item; } - new_item.set_damage( rng( damage.first, damage.second ) ); + new_item->set_damage( rng( damage.first, damage.second ) ); // no need for dirt if it's a bow - if( new_item.is_gun() && !new_item.has_flag( flag_PRIMITIVE_RANGED_WEAPON ) && - !new_item.has_flag( flag_NON_FOULING ) ) { + if( new_item->is_gun() && !new_item->has_flag( flag_PRIMITIVE_RANGED_WEAPON ) && + !new_item->has_flag( flag_NON_FOULING ) ) { int random_dirt = rng( dirt.first, dirt.second ); // if gun RNG is dirty, must add dirt fault to allow cleaning if( random_dirt > 0 ) { - new_item.set_var( "dirt", random_dirt ); - new_item.faults.emplace( "fault_gun_dirt" ); + new_item->set_var( "dirt", random_dirt ); + new_item->faults.emplace( "fault_gun_dirt" ); // chance to be unlubed, but only if it's not a laser or something - } else if( one_in( 10 ) && !new_item.has_flag( flag_NEEDS_NO_LUBE ) ) { - new_item.faults.emplace( "fault_gun_unlubricated" ); + } else if( one_in( 10 ) && !new_item->has_flag( flag_NEEDS_NO_LUBE ) ) { + new_item->faults.emplace( "fault_gun_unlubricated" ); } } // create container here from modifier or from default to get max charges later - item cont; + //TODO!: check all this function + item *cont = nullptr; if( container != nullptr ) { - cont = container->create_single( new_item.birthday() ); + cont = container->create_single( new_item->birthday() ); } - if( cont.is_null() && new_item.type->default_container.has_value() ) { - const itype_id &cont_value = new_item.type->default_container.value_or( "null" ); + if( cont == nullptr && new_item->type->default_container.has_value() ) { + const itype_id &cont_value = new_item->type->default_container.value_or( "null" ); if( !cont_value.is_null() ) { - cont = item( cont_value, new_item.birthday() ); + cont = item::spawn( cont_value, new_item->birthday() ); } } int max_capacity = -1; if( charges.first != -1 && charges.second == -1 ) { - const int max_ammo = new_item.ammo_capacity(); + const int max_ammo = new_item->ammo_capacity(); if( max_ammo > 0 ) { max_capacity = max_ammo; } } - if( max_capacity == -1 && !cont.is_null() && ( new_item.made_of( LIQUID ) || - ( !new_item.is_tool() && !new_item.is_gun() && !new_item.is_magazine() ) ) ) { - max_capacity = new_item.charges_per_volume( cont.get_container_capacity() ); + if( max_capacity == -1 && cont != nullptr && ( new_item->made_of( LIQUID ) || + ( !new_item->is_tool() && !new_item->is_gun() && !new_item->is_magazine() ) ) ) { + max_capacity = new_item->charges_per_volume( cont->get_container_capacity() ); } const bool charges_not_set = charges.first == -1 && charges.second == -1; @@ -303,80 +304,81 @@ void Item_modifier::modify( item &new_item ) const ch = charges_min == charges_max ? charges_min : rng( charges_min, charges_max ); - } else if( !cont.is_null() && new_item.made_of( LIQUID ) ) { - new_item.charges = std::max( 1, max_capacity ); + } else if( cont != nullptr && new_item->made_of( LIQUID ) ) { + new_item->charges = std::max( 1, max_capacity ); } if( ch != -1 ) { - if( new_item.count_by_charges() || new_item.made_of( LIQUID ) ) { + if( new_item->count_by_charges() || new_item->made_of( LIQUID ) ) { // food, ammo // count_by_charges requires that charges is at least 1. It makes no sense to // spawn a "water (0)" item. - new_item.charges = std::max( 1, ch ); - } else if( new_item.is_tool() ) { - const int qty = std::min( ch, new_item.ammo_capacity() ); - new_item.charges = qty; - if( !new_item.ammo_types().empty() && qty > 0 ) { - new_item.ammo_set( new_item.ammo_default(), qty ); + new_item->charges = std::max( 1, ch ); + } else if( new_item->is_tool() ) { + const int qty = std::min( ch, new_item->ammo_capacity() ); + new_item->charges = qty; + if( !new_item->ammo_types().empty() && qty > 0 ) { + new_item->ammo_set( new_item->ammo_default(), qty ); } - } else if( new_item.type->can_have_charges() ) { - new_item.charges = ch; + } else if( new_item->type->can_have_charges() ) { + new_item->charges = ch; } } - if( ch > 0 && ( new_item.is_gun() || new_item.is_magazine() ) ) { + if( ch > 0 && ( new_item->is_gun() || new_item->is_magazine() ) ) { if( ammo == nullptr ) { // In case there is no explicit ammo item defined, use the default ammo - if( !new_item.ammo_types().empty() ) { - new_item.ammo_set( new_item.ammo_default(), ch ); + if( !new_item->ammo_types().empty() ) { + new_item->ammo_set( new_item->ammo_default(), ch ); } } else { - const item am = ammo->create_single( new_item.birthday() ); - new_item.ammo_set( am.typeId(), ch ); + const item *am = ammo->create_single( new_item->birthday() ); + new_item->ammo_set( am->typeId(), ch ); } // Make sure the item is in valid state - if( new_item.ammo_data() && new_item.magazine_integral() ) { - new_item.charges = std::min( new_item.charges, new_item.ammo_capacity() ); + if( new_item->ammo_data() && new_item->magazine_integral() ) { + new_item->charges = std::min( new_item->charges, new_item->ammo_capacity() ); } else { - new_item.charges = 0; + new_item->charges = 0; } } - if( new_item.is_tool() || new_item.is_gun() || new_item.is_magazine() ) { - bool spawn_ammo = rng( 0, 99 ) < with_ammo && new_item.ammo_remaining() == 0 && ch == -1 && - ( !new_item.is_tool() || new_item.type->tool->rand_charges.empty() ); - bool spawn_mag = rng( 0, 99 ) < with_magazine && !new_item.magazine_integral() && - !new_item.magazine_current(); + if( new_item->is_tool() || new_item->is_gun() || new_item->is_magazine() ) { + bool spawn_ammo = rng( 0, 99 ) < with_ammo && new_item->ammo_remaining() == 0 && ch == -1 && + ( !new_item->is_tool() || new_item->type->tool->rand_charges.empty() ); + bool spawn_mag = rng( 0, 99 ) < with_magazine && !new_item->magazine_integral() && + !new_item->magazine_current(); if( spawn_mag ) { - new_item.put_in( item( new_item.magazine_default(), new_item.birthday() ) ); + new_item->put_in( *item::spawn( new_item->magazine_default(), new_item->birthday() ) ); } if( spawn_ammo ) { if( ammo ) { - const item am = ammo->create_single( new_item.birthday() ); - new_item.ammo_set( am.typeId() ); + const item *am = ammo->create_single( new_item->birthday() ); + new_item->ammo_set( am->typeId() ); } else { - new_item.ammo_set( new_item.ammo_default() ); + new_item->ammo_set( new_item->ammo_default() ); } } } - if( !cont.is_null() ) { - cont.put_in( new_item ); + if( cont != nullptr ) { + cont->put_in( *new_item ); new_item = cont; } if( contents != nullptr ) { - Item_spawn_data::ItemList contentitems = contents->create( new_item.birthday() ); - for( const item &it : contentitems ) { - new_item.put_in( it ); + Item_spawn_data::ItemList contentitems = contents->create( new_item->birthday() ); + for( item *&it : contentitems ) { + new_item->put_in( *it ); } } for( auto &flag : custom_flags ) { - new_item.set_flag( flag ); + new_item->set_flag( flag ); } + return new_item; } void Item_modifier::check_consistency( const std::string &context ) const @@ -501,7 +503,7 @@ Item_spawn_data::ItemList Item_group::create( const time_point &birthday, Recurs return result; } -item Item_group::create_single( const time_point &birthday, RecursionList &rec ) const +item *Item_group::create_single( const time_point &birthday, RecursionList &rec ) const { if( type == G_COLLECTION ) { for( const auto &elem : items ) { @@ -520,7 +522,7 @@ item Item_group::create_single( const time_point &birthday, RecursionList &rec ) return ( elem )->create_single( birthday, rec ); } } - return item( itype_id::NULL_ID(), birthday ); + return &null_item_reference();//item( itype_id::NULL_ID(), birthday ); } void Item_group::check_consistency( const std::string &context ) const @@ -586,16 +588,16 @@ item_group::ItemList item_group::items_from( const item_group_id &group_id ) return items_from( group_id, calendar::start_of_cataclysm ); } -item item_group::item_from( const item_group_id &group_id, const time_point &birthday ) +item *item_group::item_from( const item_group_id &group_id, const time_point &birthday ) { const auto group = item_controller->get_group( group_id ); if( group == nullptr ) { - return item(); + return nullptr; } return group->create_single( birthday ); } -item item_group::item_from( const item_group_id &group_id ) +item *item_group::item_from( const item_group_id &group_id ) { return item_from( group_id, calendar::start_of_cataclysm ); } diff --git a/src/item_group.h b/src/item_group.h index 5d6cb4864a53..a88113dcb77f 100644 --- a/src/item_group.h +++ b/src/item_group.h @@ -26,13 +26,13 @@ namespace item_group * Note that this may return a null-item, if the group does not exist, is empty or did not * create an item this time. You have to check this with @ref item::is_null. */ -item item_from( const item_group_id &group_id, const time_point &birthday ); +item *item_from( const item_group_id &group_id, const time_point &birthday ); /** * Same as above but with implicit birthday at turn 0. */ -item item_from( const item_group_id &group_id ); +item *item_from( const item_group_id &group_id ); -using ItemList = std::vector; +using ItemList = std::vector; /** * Create items from the given group. It creates as many items as the group definition requests. * For example if the group is a distribution that only contains item ids it will create @@ -104,7 +104,7 @@ item_group_id load_item_group( const JsonValue &value, const std::string &defaul class Item_spawn_data { public: - using ItemList = std::vector; + using ItemList = std::vector; using RecursionList = std::vector; Item_spawn_data( int _probability ) : probability( _probability ) { } @@ -121,8 +121,8 @@ class Item_spawn_data * The same as create, but create a single item only. * The returned item might be a null item! */ - virtual item create_single( const time_point &birthday, RecursionList &rec ) const = 0; - item create_single( const time_point &birthday ) const; + virtual item *create_single( const time_point &birthday, RecursionList &rec ) const = 0; + item *create_single( const time_point &birthday ) const; /** * Check item / spawn settings for consistency. Includes * checking for valid item types and valid settings. @@ -186,7 +186,7 @@ class Item_modifier Item_modifier(); Item_modifier( Item_modifier && ) = default; - void modify( item &new_item ) const; + item *modify( item *new_item ) const; void check_consistency( const std::string &context ) const; bool remove_item( const itype_id &itemid ); bool replace_item( const itype_id &itemid, const itype_id &replacementid ); @@ -234,7 +234,7 @@ 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 ) const override; - item create_single( const time_point &birthday, RecursionList &rec ) const override; + item *create_single( const time_point &birthday, RecursionList &rec ) const override; void check_consistency( const std::string &context ) const override; bool remove_item( const itype_id &itemid ) override; bool replace_item( const itype_id &itemid, const itype_id &replacementid ) override; @@ -250,7 +250,7 @@ class Single_item_creator : public Item_spawn_data class Item_group : public Item_spawn_data { public: - using ItemList = std::vector; + using ItemList = std::vector; enum Type { G_COLLECTION, G_DISTRIBUTION @@ -280,7 +280,7 @@ class Item_group : public Item_spawn_data void add_entry( std::unique_ptr ptr ); ItemList create( const time_point &birthday, RecursionList &rec ) const override; - item create_single( const time_point &birthday, RecursionList &rec ) const override; + item *create_single( const time_point &birthday, RecursionList &rec ) const override; void check_consistency( const std::string &context ) const override; bool remove_item( const itype_id &itemid ) override; bool replace_item( const itype_id &itemid, const itype_id &replacementid ) override; diff --git a/src/item_handling_util.h b/src/item_handling_util.h index 14b4f272db99..8e08f81d2613 100644 --- a/src/item_handling_util.h +++ b/src/item_handling_util.h @@ -4,7 +4,7 @@ #include -#include "item_location.h" +#include "safe_reference.h" class JsonIn; class JsonOut; @@ -12,9 +12,9 @@ class JsonOut; struct iuse_location { iuse_location() = default; ~iuse_location() = default; - iuse_location( const item_location &loc, int count ) : loc( loc ), count( count ) { } + iuse_location( item &loc, int count ) : loc( &loc ), count( count ) { } - item_location loc; + safe_reference loc; int count = 0; void serialize( JsonOut &jsout ) const; diff --git a/src/item_location.cpp b/src/item_location.cpp index 24c879fc7818..4698a2e5593c 100644 --- a/src/item_location.cpp +++ b/src/item_location.cpp @@ -33,6 +33,8 @@ #include "visitable.h" #include "vpart_position.h" +//TODO!: blah blah blah +/* template static int find_index( const T &sel, const item *obj ) { @@ -202,9 +204,10 @@ class item_location::impl::item_on_map : public item_location::impl } item_location obtain( Character &ch, int qty ) override { + //TODO!: cheeeck ch.moves -= obtain_cost( ch, qty ); - item obj = target()->split( qty ); + item& obj = target()->split( qty ); if( !obj.is_null() ) { return item_location( ch, &ch.i_add( obj, should_stack ) ); } else { @@ -355,7 +358,7 @@ class item_location::impl::item_on_person : public item_location::impl obj = *target(); } - if( who->is_armed() && &who->weapon == target() ) { + if( who->is_armed() && who->weapon == target() ) { // no penalties because we already have this item in our hands mv += dynamic_cast( who )->item_handling_cost( obj, false, 0 ); } else { @@ -428,14 +431,14 @@ class item_location::impl::item_on_vehicle : public item_location::impl js.member( "type", "vehicle" ); js.member( "pos", position() ); js.member( "part", cur.part ); - if( target() != &cur.veh.part( cur.part ).base ) { + if( target() != cur.veh.part( cur.part ).base ) { js.member( "idx", find_index( cur, target() ) ); } js.end_object(); } item *unpack( int idx ) const override { - return idx >= 0 ? retrieve_index( cur, idx ) : &cur.veh.part( cur.part ).base; + return idx >= 0 ? retrieve_index( cur, idx ) : cur.veh.part( cur.part ).base; } type where() const override { @@ -497,7 +500,7 @@ class item_location::impl::item_on_vehicle : public item_location::impl } void remove_item() override { - item &base = cur.veh.part( cur.part ).base; + item &base = *cur.veh.part( cur.part ).base; if( &base == target() ) { cur.veh.remove_part( cur.part ); // vehicle_part::base } else { @@ -507,7 +510,7 @@ class item_location::impl::item_on_vehicle : public item_location::impl } void make_dirty() override { - if( what.get() != &cur.veh.part( cur.part ).base ) { + if( what.get() != cur.veh.part( cur.part ).base ) { idx = find_index( cur, what.get() ); } needs_unpacking = true; @@ -554,7 +557,7 @@ class item_location::impl::item_in_container : public item_location::impl if( idx < 0 || static_cast( idx ) >= target()->contents.num_item_stacks() ) { return nullptr; } - std::list all_items = container->contents.all_items_ptr(); + std::vector all_items = container->contents.all_items_ptr(); auto iter = all_items.begin(); std::advance( iter, idx ); if( iter != all_items.end() ) { @@ -710,7 +713,7 @@ void item_location::deserialize( JsonIn &js ) } else if( type == "in_container" ) { item_location parent; obj.read( "parent", parent ); - const std::list parent_contents = parent->contents.all_items_top(); + const std::vector parent_contents = parent->contents.all_items_top(); auto iter = parent_contents.begin(); std::advance( iter, idx ); ptr.reset( new impl::item_in_container( parent, *iter ) ); @@ -784,3 +787,4 @@ void item_location::make_dirty() { ptr->make_dirty(); } +*/ diff --git a/src/item_location.h b/src/item_location.h index 4d1704c42d00..c66d91f05900 100644 --- a/src/item_location.h +++ b/src/item_location.h @@ -19,6 +19,8 @@ class JsonOut; * Provides a generic interface of querying, obtaining and removing an item * Is invalidated by many operations (including copying of the item) */ +//TODO!: remove this class entirely +#ifdef THIS_IS_DISABLED_NOW class item_location { public: @@ -97,5 +99,5 @@ class item_location std::shared_ptr ptr; }; - +#endif #endif // CATA_SRC_ITEM_LOCATION_H diff --git a/src/item_stack.cpp b/src/item_stack.cpp index 910a86fb4fdd..8e4788d74c7e 100644 --- a/src/item_stack.cpp +++ b/src/item_stack.cpp @@ -67,17 +67,14 @@ item_stack::const_reverse_iterator item_stack::rend() const item_stack::iterator item_stack::get_iterator_from_pointer( item *it ) { - return items->get_iterator_from_pointer( it ); + return std::find_if( items->begin(), items->end(), [it]( item * const & e ) { + return e == it; + } ); } item_stack::iterator item_stack::get_iterator_from_index( size_t idx ) { - return items->get_iterator_from_index( idx ); -} - -size_t item_stack::get_index_from_iterator( const item_stack::const_iterator &it ) -{ - return items->get_index_from_iterator( it ); + return std::next( items->begin(), idx ); } item &item_stack::only_item() @@ -87,19 +84,19 @@ item &item_stack::only_item() return null_item_reference(); } else if( size() > 1 ) { debugmsg( "More than one item at target location: %s", enumerate_as_string( begin(), - end(), []( const item & it ) { - return it.typeId(); + end(), []( const item * const & it ) { + return it->typeId(); } ) ); return null_item_reference(); } - return *items->begin(); + return **items->begin(); } units::volume item_stack::stored_volume() const { units::volume ret = 0_ml; - for( const item &it : *items ) { - ret += it.volume(); + for( const item * const &it : *items ) { + ret += it->volume(); } return ret; } @@ -120,9 +117,9 @@ int item_stack::amount_can_fit( const item &it ) const item *item_stack::stacks_with( const item &it ) { - for( item &here : *items ) { - if( here.stacks_with( it ) ) { - return &here; + for( item *&here : *items ) { + if( here->stacks_with( it ) ) { + return here; } } return nullptr; @@ -130,9 +127,9 @@ item *item_stack::stacks_with( const item &it ) const item *item_stack::stacks_with( const item &it ) const { - for( const item &here : *items ) { - if( here.stacks_with( it ) ) { - return &here; + for( const item * const &here : *items ) { + if( here->stacks_with( it ) ) { + return here; } } return nullptr; diff --git a/src/item_stack.h b/src/item_stack.h index b8df981669fd..428a0c87f7ce 100644 --- a/src/item_stack.h +++ b/src/item_stack.h @@ -19,20 +19,20 @@ class item_stack { protected: - cata::colony *items; + std::vector *items; public: - using iterator = cata::colony::iterator; - using const_iterator = cata::colony::const_iterator; - using reverse_iterator = cata::colony::reverse_iterator; - using const_reverse_iterator = cata::colony::const_reverse_iterator; + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + using reverse_iterator = std::vector::reverse_iterator; + using const_reverse_iterator = std::vector::const_reverse_iterator; - item_stack( cata::colony *items ) : items( items ) { } + item_stack( std::vector *items ) : items( items ) { } virtual ~item_stack() = default; size_t size() const; bool empty() const; - virtual void insert( const item &newitem ) = 0; + virtual void insert( item &newitem ) = 0; virtual iterator erase( const_iterator it ) = 0; virtual void clear(); // Will cause a debugmsg if there is not exactly one item at the location @@ -40,9 +40,9 @@ class item_stack // While iterators to colonies are stable, indexes are not. // These functions should only be used for serialization/deserialization + //TODO!: delete these entirly, they're not being used for serialization anyway iterator get_iterator_from_pointer( item *it ); iterator get_iterator_from_index( size_t idx ); - size_t get_index_from_iterator( const const_iterator &it ); iterator begin(); iterator end(); diff --git a/src/itype.cpp b/src/itype.cpp index 09597671569d..409fefbfa41e 100644 --- a/src/itype.cpp +++ b/src/itype.cpp @@ -130,7 +130,8 @@ int itype::invoke( player &p, item &it, const tripoint &pos, const std::string & // then a second time with draw explosion // the player responsible of the explosion is the one that activated the object if( iuse_name == "transform" ) { - it.activated_by = p.get_safe_reference(); + //TODO!: put this back to a safe reference once players are added + it.activated_by = &p; } return use->call( p, it, false, pos ); @@ -157,3 +158,8 @@ bool itype::can_have_charges() const } return false; } + +bool itype::is_seed() const +{ + return !!seed; +} diff --git a/src/itype.h b/src/itype.h index 9e16be988b0f..3944d1785da9 100644 --- a/src/itype.h +++ b/src/itype.h @@ -1130,6 +1130,9 @@ struct itype { int invoke( player &p, item &it, const tripoint &pos ) const; // Picks first method or returns 0 int invoke( player &p, item &it, const tripoint &pos, const std::string &iuse_name ) const; void tick( player &p, item &it, const tripoint &pos ) const; + + bool is_fuel() const; + bool is_seed() const; }; #endif // CATA_SRC_ITYPE_H diff --git a/src/iuse.cpp b/src/iuse.cpp index 9945ca92fe2c..def63105de5c 100644 --- a/src/iuse.cpp +++ b/src/iuse.cpp @@ -388,8 +388,8 @@ static std::string colorized_feature_description_at( const tripoint ¢er_poin static std::string colorized_item_name( const item &item ); static std::string colorized_item_description( const item &item ); -static item get_top_item_at_point( const tripoint &point, - const units::volume &min_visible_volume ); +static const item &get_top_item_at_point( const tripoint &point, + const units::volume &min_visible_volume ); static std::string effects_description_for_creature( Creature *creature, std::string &pose, const std::string &pronoun_sex ); @@ -420,7 +420,7 @@ void remove_radio_mod( item &it, player &p ) return; } p.add_msg_if_player( _( "You remove the radio modification from your %s!" ), it.tname() ); - item mod( "radio_mod" ); + item &mod = *item::spawn( "radio_mod" ); p.i_add_or_drop( mod, 1 ); it.unset_flag( "RADIO_ACTIVATION" ); it.unset_flag( "RADIO_MOD" ); @@ -541,27 +541,27 @@ int iuse::smoking( player *p, item *it, bool, const tripoint & ) return 0; } - item cig; + item *cig; if( it->typeId() == itype_cig ) { - cig = item( "cig_lit", calendar::turn ); - cig.item_counter = to_turns( 4_minutes ); + cig = item::spawn( "cig_lit", calendar::turn ); + cig->item_counter = to_turns( 4_minutes ); p->mod_stored_kcal( 30 ); p->mod_thirst( 2 ); } else if( it->typeId() == itype_handrolled_cig ) { // This transforms the hand-rolled into a normal cig, which isn't exactly // what I want, but leaving it for now. - cig = item( "cig_lit", calendar::turn ); - cig.item_counter = to_turns( 4_minutes ); + cig = item::spawn( "cig_lit", calendar::turn ); + cig->item_counter = to_turns( 4_minutes ); p->mod_thirst( 2 ); p->mod_stored_kcal( 30 ); } else if( it->typeId() == itype_cigar ) { - cig = item( "cigar_lit", calendar::turn ); - cig.item_counter = to_turns( 12_minutes ); + cig = item::spawn( "cigar_lit", calendar::turn ); + cig->item_counter = to_turns( 12_minutes ); p->mod_thirst( 3 ); p->mod_stored_kcal( 40 ); } else if( it->typeId() == itype_joint ) { - cig = item( "joint_lit", calendar::turn ); - cig.item_counter = to_turns( 4_minutes ); + cig = item::spawn( "joint_lit", calendar::turn ); + cig->item_counter = to_turns( 4_minutes ); p->mod_stored_kcal( -40 ); p->mod_thirst( 6 ); if( p->get_painkiller() < 5 ) { @@ -575,9 +575,9 @@ int iuse::smoking( player *p, item *it, bool, const tripoint & ) } // If we're here, we better have a cig to light. p->use_charges_if_avail( itype_fire, 1 ); - cig.active = true; - p->inv.add_item( cig, false, true ); - p->add_msg_if_player( m_neutral, _( "You light a %s." ), cig.tname() ); + cig->active = true; + p->inv.add_item( *cig, false, true ); + p->add_msg_if_player( m_neutral, _( "You light a %s." ), cig->tname() ); // Parting messages if( it->typeId() == itype_joint ) { @@ -603,8 +603,8 @@ int iuse::ecig( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_neutral, _( "You inhale some vapor from your advanced electronic cigarette." ) ); p->use_charges( itype_nicotine_liquid, 1 ); - item dummy_ecig = item( "ecig", calendar::turn ); - p->consume_effects( dummy_ecig ); + item *dummy_ecig = item::spawn_temporary( "ecig", calendar::turn ); + p->consume_effects( *dummy_ecig ); } else { p->add_msg_if_player( m_info, _( "You don't have any nicotine liquid!" ) ); return 0; @@ -859,8 +859,8 @@ int iuse::vaccine( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_good, _( "You feel tough." ) ); p->mod_healthy_mod( 200, 200 ); p->mod_pain( 3 ); - item syringe( "syringe", it->birthday() ); - p->i_add( syringe ); + item *syringe = item::spawn( "syringe", it->birthday() ); + p->i_add( *syringe ); return it->type->charges_to_use(); } @@ -1225,8 +1225,8 @@ int iuse::purify_smart( player *p, item *it, bool, const tripoint & ) p->mod_pain( 3 ); - item syringe( "syringe", it->birthday() ); - p->i_add( syringe ); + item *syringe = item::spawn( "syringe", it->birthday() ); + p->i_add( *syringe ); return it->type->charges_to_use(); } @@ -1657,7 +1657,7 @@ int iuse::radio_mod( player *p, item *, bool, const tripoint & ) }; // note: if !p->is_npc() then p is avatar - item_location loc = game_menus::inv::titled_filter_menu( + item* loc = game_menus::inv::titled_filter_menu( filter, *p->as_avatar(), _( "Modify what?" ) ); if( !loc ) { @@ -1712,7 +1712,7 @@ int iuse::remove_all_mods( player *p, item *, bool, const tripoint & ) return 0; } - item_location loc = g->inv_map_splice( []( const item & e ) { + item* loc = g->inv_map_splice( []( const item & e ) { for( const item *it : e.toolmods() ) { if( !it->is_irremovable() ) { return true; @@ -1728,7 +1728,7 @@ int iuse::remove_all_mods( player *p, item *, bool, const tripoint & ) return 0; } - if( !loc->ammo_remaining() || p->unload( loc ) ) { + if( !loc->ammo_remaining() || p->unload( *loc ) ) { item *mod = loc->contents.get_item_with( []( const item & e ) { return e.is_toolmod() && !e.is_irremovable(); @@ -1783,7 +1783,7 @@ int iuse::fishing_rod( player *p, item *it, bool, const tripoint & ) } p->add_msg_if_player( _( "You cast your line and wait to hook something…" ) ); p->assign_activity( ACT_FISH, to_moves( 5_hours ), 0, 0, it->tname() ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.push_back( it ); p->activity.coord_set = g->get_fishable_locations( 60, *found ); return 0; } @@ -2032,16 +2032,16 @@ int iuse::unpack_item( player *p, item *it, bool, const tripoint & ) int iuse::pack_cbm( player *p, item *it, bool, const tripoint & ) { - item_location bionic = g->inv_map_splice( []( const item & e ) { + item* bionic = g->inv_map_splice( []( const item & e ) { return e.is_bionic() && e.has_flag( "NO_PACKED" ); }, _( "Choose CBM to pack" ), PICKUP_RANGE, _( "You don't have any CBMs." ) ); if( !bionic ) { return 0; } - if( !bionic.get_item()->faults.empty() ) { + if( !bionic->faults.empty() ) { if( p->query_yn( _( "This CBM is faulty. You should mend it first. Do you want to try?" ) ) ) { - p->mend_item( std::move( bionic ) ); + p->mend_item( *bionic ); } return 0; } @@ -2049,7 +2049,7 @@ int iuse::pack_cbm( player *p, item *it, bool, const tripoint & ) const int success = p->get_skill_level( skill_firstaid ) - rng( 0, 6 ); if( success > 0 ) { p->add_msg_if_player( m_good, _( "You carefully prepare the CBM for sterilization." ) ); - bionic.get_item()->unset_flag( "NO_PACKED" ); + bionic->unset_flag( "NO_PACKED" ); } else { p->add_msg_if_player( m_bad, _( "You fail to properly prepare the CBM." ) ); } @@ -2135,9 +2135,9 @@ int iuse::directional_antenna( player *p, item *it, bool, const tripoint & ) // If we don't wield the radio, also check on the ground if( radios.empty() ) { map_stack items = get_map().i_at( p->pos() ); - for( item &an_item : items ) { - if( an_item.typeId() == itype_radio_on ) { - radios.push_back( &an_item ); + for( item * const &an_item : items ) { + if( an_item->typeId() == itype_radio_on ) { + radios.push_back( an_item ); } } } @@ -2145,7 +2145,7 @@ int iuse::directional_antenna( player *p, item *it, bool, const tripoint & ) add_msg( m_info, _( "Must have an active radio to check for signal direction." ) ); return 0; } - const item radio = *radios.front(); + const item &radio = *radios.front(); // Find the radio station its tuned to (if any) const auto tref = overmap_buffer.find_radio_station( radio.frequency ); if( !tref ) { @@ -2294,16 +2294,16 @@ int iuse::note_bionics( player *p, item *it, bool t, const tripoint &pos ) if( !here.has_items( pt ) || !p->sees( pt ) ) { continue; } - for( item &corpse : here.i_at( pt ) ) { - if( !corpse.is_corpse() || - corpse.get_var( "bionics_scanned_by", -1 ) == p->getID().get_value() ) { + for( item * const &corpse : here.i_at( pt ) ) { + if( !corpse->is_corpse() || + corpse->get_var( "bionics_scanned_by", -1 ) == p->getID().get_value() ) { continue; } std::vector cbms; - for( const item &maybe_cbm : corpse.components ) { - if( maybe_cbm.is_bionic() ) { - cbms.push_back( &maybe_cbm ); + for( const item * const &maybe_cbm : corpse->components ) { + if( maybe_cbm->is_bionic() ) { + cbms.push_back( maybe_cbm ); } } @@ -2319,9 +2319,9 @@ int iuse::note_bionics( player *p, item *it, bool t, const tripoint &pos ) return 0; } - corpse.set_var( "bionics_scanned_by", p->getID().get_value() ); + corpse->set_var( "bionics_scanned_by", p->getID().get_value() ); if( !cbms.empty() ) { - corpse.set_flag( "CBM_SCANNED" ); + corpse->set_flag( "CBM_SCANNED" ); std::string bionics_string = enumerate_as_string( cbms.begin(), cbms.end(), []( const item * entry ) -> std::string { @@ -2329,7 +2329,7 @@ int iuse::note_bionics( player *p, item *it, bool t, const tripoint &pos ) }, enumeration_conjunction::none ); //~ %1 is corpse name, %2 is direction, %3 is bionic name p->add_msg_if_player( m_good, _( "A %1$s located %2$s contains %3$s." ), - corpse.display_name().c_str(), + corpse->display_name().c_str(), direction_name( direction_from( p->pos(), pt ) ).c_str(), bionics_string.c_str() ); @@ -3278,7 +3278,7 @@ int iuse::jackhammer( player *p, item *it, bool, const tripoint &pos ) moves = moves * ( 10 - helpers.size() ) / 10; p->assign_activity( ACT_JACKHAMMER, moves ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.push_back( it ); p->activity.placement = g->m.getabs( pnt ); p->add_msg_if_player( _( "You start drilling into the %1$s with your %2$s." ), g->m.tername( pnt ), it->tname() ); @@ -3333,7 +3333,7 @@ int iuse::pickaxe( player *p, item *it, bool, const tripoint &pos ) moves = moves * ( 10 - helpers.size() ) / 10; p->assign_activity( ACT_PICKAXE, moves, -1 ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.push_back( it ); p->activity.placement = g->m.getabs( pnt ); p->add_msg_if_player( _( "You strike the %1$s with your %2$s." ), g->m.tername( pnt ), it->tname() ); @@ -3791,9 +3791,9 @@ int iuse::arrow_flammable( player *p, item *it, bool, const tripoint & ) it->convert( itype_arrow_flamming ); return 0; } - item lit_arrow( *it ); - lit_arrow.convert( itype_arrow_flamming ).charges = 1; - p->i_add( lit_arrow ); + item *lit_arrow = item::spawn( *it ); + lit_arrow->convert( itype_arrow_flamming ).charges = 1; + p->i_add( *lit_arrow ); return 1; } @@ -4047,9 +4047,9 @@ int iuse::tazer2( player *p, item *it, bool b, const tripoint &pos ) if( it->ammo_remaining() >= 100 ) { // Instead of having a ctrl+c+v of the function above, spawn a fake tazer and use it // Ugly, but less so than copied blocks - item fake( "tazer", calendar::start_of_cataclysm ); - fake.charges = 100; - return tazer( p, &fake, b, pos ); + item *fake = item::spawn( "tazer", calendar::start_of_cataclysm ); + fake->charges = 100; + return tazer( p, fake, b, pos ); } else { p->add_msg_if_player( m_info, _( "Insufficient power" ) ); } @@ -4439,7 +4439,7 @@ int iuse::portable_game( player *p, item *it, bool t, const tripoint & ) p->add_msg_if_player( _( "You play on your %s for a while." ), it->tname() ); p->assign_activity( ACT_GAME, moves, -1, 0, "gaming" ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.push_back( it ); std::string end_message; end_message.clear(); int game_score = 0; @@ -4480,7 +4480,7 @@ int iuse::hand_crank( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( _( "You start cranking the %s to charge its %s." ), it->tname(), it->magazine_current()->tname() ); p->assign_activity( ACT_HAND_CRANK, moves, -1, 0, "hand-cranking" ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.push_back( it ); } else { p->add_msg_if_player( _( "You could use the %s to charge its %s, but it's already charged." ), it->tname(), magazine->tname() ); @@ -4527,7 +4527,7 @@ int iuse::vibe( player *p, item *it, bool, const tripoint & ) it->tname() ); } p->assign_activity( ACT_VIBE, moves, -1, 0, "de-stressing" ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.push_back( it ); } return it->type->charges_to_use(); } @@ -4611,20 +4611,20 @@ int iuse::blood_draw( player *p, item *it, bool, const tripoint & ) return 0; } - item blood( "blood", calendar::turn ); bool drew_blood = false; bool acid_blood = false; + const mtype *blood_mtype = nullptr; for( auto &map_it : g->m.i_at( point( p->posx(), p->posy() ) ) ) { - if( map_it.is_corpse() && + if( map_it->is_corpse() && query_yn( _( "Draw blood from %s?" ), - colorize( map_it.tname(), map_it.color_in_inventory() ) ) ) { - p->add_msg_if_player( m_info, _( "You drew blood from the %s…" ), map_it.tname() ); + colorize( map_it->tname(), map_it->color_in_inventory() ) ) ) { + p->add_msg_if_player( m_info, _( "You drew blood from the %s…" ), map_it->tname() ); drew_blood = true; - auto bloodtype( map_it.get_mtype()->bloodType() ); + auto bloodtype( map_it->get_mtype()->bloodType() ); if( bloodtype.obj().has_acid ) { acid_blood = true; } else { - blood.set_mtype( map_it.get_mtype() ); + blood_mtype = map_it->get_mtype(); } } } @@ -4641,8 +4641,8 @@ int iuse::blood_draw( player *p, item *it, bool, const tripoint & ) } if( acid_blood ) { - item acid( "acid", calendar::turn ); - it->put_in( acid ); + item *acid = item::spawn( "acid", calendar::turn ); + it->put_in( *acid ); if( one_in( 3 ) ) { if( it->inc_damage( DT_ACID ) ) { p->add_msg_if_player( m_info, _( "…but acidic blood melts the %s, destroying it!" ), @@ -4659,7 +4659,11 @@ int iuse::blood_draw( player *p, item *it, bool, const tripoint & ) return it->type->charges_to_use(); } - it->put_in( blood ); + item *blood = item::spawn( "blood", calendar::turn ); + if( blood_mtype != nullptr ) { + blood->set_mtype( blood_mtype ); + } + it->put_in( *blood ); return it->type->charges_to_use(); } @@ -4671,15 +4675,15 @@ int iuse::mind_splicer( player *p, item *it, bool, const tripoint & ) return 0; } for( auto &map_it : g->m.i_at( point( p->posx(), p->posy() ) ) ) { - if( map_it.typeId() == itype_rmi2_corpse && - query_yn( _( "Use the mind splicer kit on the %s?" ), colorize( map_it.tname(), - map_it.color_in_inventory() ) ) ) { + if( map_it->typeId() == itype_rmi2_corpse && + query_yn( _( "Use the mind splicer kit on the %s?" ), colorize( map_it->tname(), + map_it->color_in_inventory() ) ) ) { auto filter = []( const item & it ) { return it.typeId() == itype_data_card; }; avatar *you = p->as_avatar(); - item_location loc; + item* loc=nullptr; if( you != nullptr ) { loc = game_menus::inv::titled_filter_menu( filter, *you, _( "Select storage media" ) ); } @@ -4694,7 +4698,7 @@ int iuse::mind_splicer( player *p, item *it, bool, const tripoint & ) skill_firstaid ) - 1 ) - 10_minutes * ( p->get_dex() - 8 ), 30_minutes ); player_activity act( ACT_MIND_SPLICER, to_moves( time ) ); - act.targets.push_back( item_location( *p, &data_card ) ); + act.targets.push_back( &data_card ); p->assign_activity( act ); return it->type->charges_to_use(); } @@ -4727,8 +4731,8 @@ int iuse::lumber( player *p, item *it, bool t, const tripoint & ) } // Check if player is standing on any lumber for( auto &i : g->m.i_at( p->pos() ) ) { - if( i.typeId() == itype_log ) { - g->m.i_rem( p->pos(), &i ); + if( i->typeId() == itype_log ) { + g->m.i_rem( p->pos(), i ); cut_log_into_planks( *p ); return it->type->charges_to_use(); } @@ -4736,7 +4740,7 @@ int iuse::lumber( player *p, item *it, bool t, const tripoint & ) // If the player is not standing on a log, check inventory avatar *you = p->as_avatar(); - item_location loc; + item*loc=nullptr; auto filter = []( const item & it ) { return it.typeId() == itype_log; }; @@ -4946,7 +4950,7 @@ int iuse::oxytorch( player *p, item *it, bool, const tripoint & ) // placing ter here makes resuming tasks work better p->assign_activity( ACT_OXYTORCH, moves, static_cast( ter ) ); - p->activity.targets.push_back( item_location( *p, it ) ); + p->activity.targets.push_back( it ); p->activity.placement = pnt; p->activity.values.push_back( charges ); @@ -5107,8 +5111,8 @@ int iuse::mop( player *p, item *it, bool, const tripoint & ) const std::function f = [&to_check]( const tripoint & pnt ) { if( !g->m.has_flag( "LIQUIDCONT", pnt ) && !g->m.has_flag( "SEALED", pnt ) ) { map_stack items = g->m.i_at( pnt ); - auto found = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.made_of( LIQUID ); + auto found = std::find_if( items.begin(), items.end(), []( const item * const & it ) { + return it->made_of( LIQUID ); } ); if( found != items.end() ) { return true; @@ -5128,8 +5132,8 @@ int iuse::mop( player *p, item *it, bool, const tripoint & ) return true; } vehicle_stack items = veh->get_items( elem ); - auto found = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.made_of( LIQUID ); + auto found = std::find_if( items.begin(), items.end(), []( const item * const & it ) { + return it->made_of( LIQUID ); } ); if( found != items.end() ) { return true; @@ -5695,8 +5699,8 @@ int iuse::adrenaline_injector( player *p, item *it, bool, const tripoint & ) p->add_msg_player_or_npc( _( "You inject yourself with adrenaline." ), _( " injects themselves with adrenaline." ) ); - item syringe( "syringe", it->birthday() ); - p->i_add( syringe ); + item *syringe = item::spawn( "syringe", it->birthday() ); + p->i_add( *syringe ); if( p->has_effect( effect_adrenaline ) ) { p->add_msg_if_player( m_bad, _( "Your heart spasms!" ) ); // Note: not the mod, the health @@ -5849,7 +5853,7 @@ int iuse::gun_repair( player *p, item *it, bool, const tripoint & ) p->add_msg_if_player( m_info, _( "You need a mechanics skill of 2 to use this repair kit." ) ); return 0; } - item_location loc = game_menus::inv::titled_menu( g->u, ( "Select the firearm to repair" ) ); + item* loc = game_menus::inv::titled_menu( g->u, ( "Select the firearm to repair" ) ); if( !loc ) { p->add_msg_if_player( m_info, _( "You do not have that item!" ) ); return 0; @@ -5966,13 +5970,13 @@ int iuse::toolmod_attach( player *p, item *it, bool, const tripoint & ) } if( loc->ammo_remaining() ) { - if( !p->unload( loc ) ) { + if( !p->unload( *loc ) ) { p->add_msg_if_player( m_info, _( "You cancel unloading the tool." ) ); return 0; } } - p->toolmod_add( std::move( loc ), item_location( *p, it ) ); + p->toolmod_add( *loc, *it ); return 0; } @@ -6604,7 +6608,7 @@ int iuse::einktabletpc( player *p, item *it, bool t, const tripoint &pos ) } avatar *you = p->as_avatar(); - item_location loc; + item* loc=nullptr; auto filter = []( const item & it ) { return it.has_flag( "MC_MOBILE" ); }; @@ -6760,18 +6764,19 @@ static std::string colorized_item_description( const item &item ) return item.info( dummy, &query, 1 ); } -static item get_top_item_at_point( const tripoint &point, - const units::volume &min_visible_volume ) +static const item &get_top_item_at_point( const tripoint &point, + const units::volume &min_visible_volume ) { map_stack items = g->m.i_at( point ); // iterate from topmost item down to ground - for( const item &it : items ) { - if( it.volume() > min_visible_volume ) { + for( const item * const &it : items ) { + if( it->volume() > min_visible_volume ) { // return top (or first big enough) item to the list - return it; + return *it; } } - return item(); + //TODO!: check + return null_item_reference(); } static std::string colorized_ter_name_flags_at( const tripoint &point, @@ -6820,7 +6825,7 @@ static std::string colorized_feature_description_at( const tripoint ¢er_poin furn_str += string_format( _( " with message \"%s\"" ), sign_message ); } if( !furn->has_flag( "CONTAINER" ) && !furn->has_flag( "SEALED" ) ) { - const item item = get_top_item_at_point( center_point, min_visible_volume ); + const item &item = get_top_item_at_point( center_point, min_visible_volume ); if( !item.is_null() ) { furn_str += string_format( _( " with %s on it" ), colorized_item_name( item ) ); item_found = true; @@ -6982,7 +6987,7 @@ static object_names_collection enumerate_objects_around_point( const tripoint &p std::string furn_desc = colorized_feature_description_at( point_around_figure, item_found, volume_to_search ); - const item item = get_top_item_at_point( point_around_figure, volume_to_search ); + const item &item = get_top_item_at_point( point_around_figure, volume_to_search ); const optional_vpart_position veh_part_pos = g->m.veh_at( point_around_figure ); std::string unusual_ter_desc = colorized_ter_name_flags_at( point_around_figure, @@ -7208,7 +7213,7 @@ static extended_photo_def photo_def_for_camera_point( const tripoint &aim_point, bool found_item_aim_point; std::string furn_desc = colorized_feature_description_at( aim_point, found_item_aim_point, 0_ml ); - const item item = get_top_item_at_point( aim_point, 0_ml ); + const item &item = get_top_item_at_point( aim_point, 0_ml ); const std::string trap_name = colorized_trap_name_at( aim_point ); std::string ter_name = colorized_ter_name_flags_at( aim_point, {}, {} ); const std::string field_desc = colorized_field_description_at( aim_point ); @@ -7729,7 +7734,7 @@ int iuse::camera( player *p, item *it, bool, const tripoint & ) p->moves -= to_moves( 2_seconds ); avatar *you = p->as_avatar(); - item_location loc; + item* loc=nullptr; if( you != nullptr ) { loc = game_menus::inv::titled_filter_menu( []( const item & it ) { return it.has_flag( "MC_MOBILE" ); @@ -7794,7 +7799,7 @@ int iuse::ehandcuffs( player *p, item *it, bool t, const tripoint &pos ) it->unset_flag( "NO_UNWIELD" ); it->active = false; - if( p->has_item( *it ) && p->weapon.typeId() == itype_e_handcuffs ) { + if( p->has_item( *it ) && p->get_weapon().typeId() == itype_e_handcuffs ) { add_msg( m_good, _( "%s on your hands opened!" ), it->tname() ); } @@ -7826,7 +7831,7 @@ int iuse::ehandcuffs( player *p, item *it, bool t, const tripoint &pos ) if( ( it->ammo_remaining() > it->type->maximum_charges() - 1000 ) && ( p2.x != pos.x || p2.y != pos.y ) ) { - if( p->has_item( *it ) && p->weapon.typeId() == itype_e_handcuffs ) { + if( p->has_item( *it ) && p->get_weapon().typeId() == itype_e_handcuffs ) { if( p->is_elec_immune() ) { if( one_in( 10 ) ) { @@ -7928,7 +7933,7 @@ int iuse::radiocar( player *p, item *it, bool, const tripoint & ) if( bomb_it == nullptr ) { //arming car with bomb avatar *you = p->as_avatar(); - item_location loc; + item* loc=nullptr; if( you != nullptr ) { loc = game_menus::inv::titled_filter_menu( []( const item & it ) { return it.has_flag( "RADIOCARITEM" ); @@ -8412,12 +8417,12 @@ int iuse::multicooker( player *p, item *it, bool t, const tripoint &pos ) } if( cooktime <= 0 ) { - item meal( it->get_var( "DISH" ) ); + item *meal = item::spawn( it->get_var( "DISH" ) ); it->active = false; it->erase_var( "DISH" ); it->erase_var( "COOKTIME" ); - it->put_in( meal ); + it->put_in( *meal ); //~ sound of a multi-cooker finishing its cycle! sounds::sound( pos, 8, sounds::sound_t::alarm, _( "ding!" ), true, "misc", "ding" ); @@ -8544,11 +8549,14 @@ int iuse::multicooker( player *p, item *it, bool t, const tripoint &pos ) inventory crafting_inv = g->u.crafting_inventory(); //add some tools and qualities. we can't add this qualities to json, because multicook must be used only by activating, not as component other crafts. - const time_point bday = calendar::start_of_cataclysm; - crafting_inv.push_back( item( "hotplate", bday ) ); //hotplate inside - crafting_inv.push_back( item( "tongs", bday ) ); //some recipes requires tongs - crafting_inv.push_back( item( "toolset", bday ) ); //toolset with CUT and other qualities inside - crafting_inv.push_back( item( "pot", bday ) ); //good COOK, BOIL, CONTAIN qualities inside + //TODO!:CHECK restore these + + //const time_point bday = calendar::start_of_cataclysm; + + //crafting_inv.push_back( item( "hotplate", bday ) ); //hotplate inside + //crafting_inv.push_back( item( "tongs", bday ) ); //some recipes requires tongs + //crafting_inv.push_back( item( "toolset", bday ) ); //toolset with CUT and other qualities inside + //crafting_inv.push_back( item( "pot", bday ) ); //good COOK, BOIL, CONTAIN qualities inside int counter = 0; @@ -8797,10 +8805,11 @@ int iuse::tow_attach( player *p, item *it, bool, const tripoint & ) } const vpart_id vpid( it->typeId().str() ); point vcoords = source_vp->mount(); - vehicle_part source_part( vpid, vcoords, item( *it ) ); + //TODO!: checksss + vehicle_part source_part( vpid, vcoords, *item::spawn( *it ) ); source_veh->install_part( vcoords, source_part ); vcoords = target_vp->mount(); - vehicle_part target_part( vpid, vcoords, item( *it ) ); + vehicle_part target_part( vpid, vcoords, *item::spawn( *it ) ); target_veh->install_part( vcoords, target_part ); if( p->has_item( *it ) ) { @@ -8824,7 +8833,7 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) const bool wearing_solar_pack = has_solar_pack || has_solar_pack_on; const bool has_ups = p->has_charges( itype_UPS_off, 1 ) || p->has_charges( itype_adv_UPS_off, 1 ); - item_location loc; + item* loc=nullptr; avatar *you = p->as_avatar(); auto filter = [&]( const item & itm ) { @@ -9025,7 +9034,7 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) const vpart_id vpid( it->typeId().str() ); point vcoords = source_vp->mount(); - vehicle_part source_part( vpid, vcoords, item( *it ) ); + vehicle_part source_part( vpid, vcoords, *item::spawn( *it ) ); if( grid_connection != nullptr ) { source_part.target.first = target_global.raw(); source_part.target.second = target_global.raw(); @@ -9052,7 +9061,7 @@ int iuse::cable_attach( player *p, item *it, bool, const tripoint & ) if( target_vp ) { vcoords = target_vp->mount(); - vehicle_part target_part( vpid, vcoords, item( *it ) ); + vehicle_part target_part( vpid, vcoords, *item::spawn( *it ) ); target_part.target.first = source_global; target_part.target.second = source_veh->global_square_location().raw(); target_veh->install_part( vcoords, target_part ); @@ -9568,21 +9577,21 @@ int iuse::craft( player *p, item *it, bool, const tripoint &pos ) pgettext( "in progress craft", " starts working on the %s." ), craft_name ); p->assign_activity( ACT_CRAFT ); // Horrible! We have to find the item again... - item_location where; + item* where=nullptr; if( p->has_item( *it ) ) { - where = item_location( *p, it ); + where = it; } else if( const cata::optional vp = g->m.veh_at( pos ).part_with_feature( "CARGO", false ) ) { const vehicle_cursor vc = vehicle_cursor( vp->vehicle(), vp->part_index() ); if( vc.has_item( *it ) ) { - where = item_location( vc, it ) ; + where = it; } } if( !where ) { map_cursor mc = map_cursor( pos ); if( mc.has_item( *it ) ) { - where = item_location( map_cursor( pos ), it ); + where = it; } else { debugmsg( "Incomplete item couldn't be found" ); return 0; @@ -9611,7 +9620,7 @@ int iuse::disassemble( player *p, item *it, bool, const tripoint & ) return 0; } - crafting::disassemble( *p->as_avatar(), item_location( *p, it ) ); + crafting::disassemble( *p->as_avatar(), *it ); return 0; } diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp index 5ea7f1241915..150c8c9b216e 100644 --- a/src/iuse_actor.cpp +++ b/src/iuse_actor.cpp @@ -262,10 +262,12 @@ int iuse_transform::use( player &p, item &it, bool t, const tripoint &pos ) cons p.moves -= moves; } - item obj_copy( it ); - item *obj; // defined here to allow making a new item assigned to the pointer - item obj_it; + item *obj; + + if( p.is_worn( it ) ) { + p.on_item_takeoff( it ); + } if( container.is_empty() ) { obj = &it.convert( target ); if( ammo_qty >= 0 || !random_ammo_qty.empty() ) { @@ -286,15 +288,14 @@ int iuse_transform::use( player &p, item &it, bool t, const tripoint &pos ) cons } } else { it.convert( container ); - obj_it = item( target, calendar::turn, std::max( ammo_qty, 1 ) ); - obj = &obj_it; + obj = item::spawn( target, calendar::turn, std::max( ammo_qty, 1 ) ); it.put_in( *obj ); } if( p.is_worn( *obj ) ) { p.reset_encumbrance(); // This is most likely wrong: it doubles temperature shift for the turn! p.update_bodytemp( get_map(), get_weather() ); - p.on_worn_item_transform( obj_copy, *obj ); + p.on_item_wear( *obj ); } obj->item_counter = countdown > 0 ? countdown : obj->type->countdown_interval; obj->active = active || obj->item_counter; @@ -348,8 +349,9 @@ void iuse_transform::finalize( const itype_id & ) debugmsg( "Invalid transform container: %s", container.c_str() ); } - item dummy( target ); - if( ammo_qty > 1 && !dummy.count_by_charges() ) { + //TODO!: push to item type + item *dummy = item::spawn_temporary( target ); + if( ammo_qty > 1 && !dummy->count_by_charges() ) { debugmsg( "Transform target with container must be an item with charges, got non-charged: %s", target.c_str() ); } @@ -358,7 +360,7 @@ void iuse_transform::finalize( const itype_id & ) void iuse_transform::info( const item &it, std::vector &dump ) const { - item dummy( target, calendar::turn, std::max( ammo_qty, 1 ) ); + item &dummy = *item::spawn_temporary( target, calendar::turn, std::max( ammo_qty, 1 ) ); if( it.has_flag( "FIT" ) ) { dummy.set_flag( "FIT" ); } @@ -388,31 +390,31 @@ void unpack_actor::load( const JsonObject &obj ) int unpack_actor::use( player &p, item &it, bool, const tripoint & ) const { - std::vector items = item_group::items_from( unpack_group, calendar::turn ); - item last_armor; + std::vector items = item_group::items_from( unpack_group, calendar::turn ); + item *last_armor = &null_item_reference(); p.add_msg_if_player( _( "You unpack the %s." ), it.tname() ); map &here = get_map(); - for( item &content : items ) { - if( content.is_armor() ) { + for( item * const &content : items ) { + if( content->is_armor() ) { if( items_fit ) { - content.set_flag( "FIT" ); - } else if( content.typeId() == last_armor.typeId() ) { - if( last_armor.has_flag( "FIT" ) ) { - content.set_flag( "FIT" ); - } else if( !last_armor.has_flag( "FIT" ) ) { - content.unset_flag( "FIT" ); + content->set_flag( "FIT" ); + } else if( content->typeId() == last_armor->typeId() ) { + if( last_armor->has_flag( "FIT" ) ) { + content->set_flag( "FIT" ); + } else if( !last_armor->has_flag( "FIT" ) ) { + content->unset_flag( "FIT" ); } } last_armor = content; } - if( content.get_storage() >= filthy_vol_threshold && it.has_flag( "FILTHY" ) ) { - content.set_flag( "FILTHY" ); + if( content->get_storage() >= filthy_vol_threshold && it.has_flag( "FILTHY" ) ) { + content->set_flag( "FILTHY" ); } - here.add_item_or_charges( p.pos(), content ); + here.add_item_or_charges( p.pos(), *content ); } p.i_rem( &it ); @@ -564,7 +566,7 @@ int explosion_iuse::use( player &p, item &it, bool t, const tripoint &pos ) cons } return 0; } - trigger_explosion( pos, it.activated_by.get() ); + trigger_explosion( pos, it.activated_by ); return 1; } @@ -856,8 +858,8 @@ int consume_drug_iuse::use( player &p, item &it, bool, const tripoint & ) const } if( !used_up_item.empty() ) { - item used_up( used_up_item, it.birthday() ); - p.i_add_or_drop( used_up ); + item *used_up = item::spawn( used_up_item, it.birthday() ); + p.i_add_or_drop( *used_up ); } p.moves -= moves; @@ -939,14 +941,14 @@ int set_transform_iuse::use( player &p, item &it, bool t, const tripoint &pos ) iuse_transform::use( p, it, t, pos ); for( auto &elem : p.worn ) { - if( elem.has_flag( flag ) && elem.active == turn_off ) { - if( elem.type->can_use( "set_transformed" ) ) { + if( elem->has_flag( flag ) && elem->active == turn_off ) { + if( elem->type->can_use( "set_transformed" ) ) { const set_transformed_iuse *actor = dynamic_cast - ( elem.get_use( "set_transformed" )->get_actor_ptr() ); + ( elem->get_use( "set_transformed" )->get_actor_ptr() ); if( actor == nullptr ) { debugmsg( "iuse_actor type descriptor and actual type mismatch" ); } else { - actor->bypass( p, elem, t, pos ); + actor->bypass( p, *elem, t, pos ); } } else { debugmsg( "Expected set_transformed function" ); @@ -1043,7 +1045,8 @@ int place_monster_iuse::use( player &p, item &it, bool, const tripoint & ) const p.moves -= moves; if( !newmon.has_flag( MF_INTERIOR_AMMO ) ) { for( auto &amdef : newmon.ammo ) { - item ammo_item( amdef.first, calendar::start_of_cataclysm ); + //TODO!: I think this is temp + item &ammo_item = *item::spawn_temporary( amdef.first, calendar::start_of_cataclysm ); const int available = p.charges_of( amdef.first ); if( available == 0 ) { amdef.second = 0; @@ -1196,7 +1199,7 @@ int pick_lock_actor::use( player &p, item &it, bool, const tripoint &t ) const ( p.dex_cur + 5 ) * 2300 ); p.assign_activity( activity_id( "ACT_LOCKPICK" ), duration, -1, p.get_item_position( &it ) ); - p.activity.targets.push_back( item_location( p, &it ) ); + p.activity.targets.push_back( &it ); p.activity.placement = pnt; return it.type->charges_to_use(); @@ -1401,8 +1404,8 @@ bool firestarter_actor::prep_firestarter_use( const player &p, tripoint &pos ) } // Check for a brazier. bool has_unactivated_brazier = false; - for( const item &i : here.i_at( pos ) ) { - if( i.typeId() == itype_brazier ) { + for( const item * const &i : here.i_at( pos ) ) { + if( i->typeId() == itype_brazier ) { has_unactivated_brazier = true; } } @@ -1514,7 +1517,7 @@ int firestarter_actor::use( player &p, item &it, bool t, const tripoint &spos ) moves_modifier + moves_cost_fast / 100.0 + 2; p.assign_activity( ACT_START_FIRE, moves, potential_skill_gain, 0, it.tname() ); - p.activity.targets.push_back( item_location( p, &it ) ); + p.activity.targets.push_back( &it ); p.activity.values.push_back( g->natural_light_level( pos.z ) ); p.activity.placement = pos; // charges to use are handled by the activity @@ -1549,12 +1552,12 @@ int salvage_actor::use( player &p, item &it, bool t, const tripoint & ) const return 0; } - if( !try_to_cut_up( p, *item_loc.get_item() ) ) { + if( !try_to_cut_up( p, *item_loc ) ) { // Messages should have already been displayed. return 0; } - return cut_up( p, it, item_loc ); + return cut_up( p, it, *item_loc ); } static const units::volume minimal_volume_to_cut = 250_ml; @@ -1619,7 +1622,7 @@ bool salvage_actor::try_to_cut_up( player &p, item &it ) const return false; } // Softer warnings at the end so we don't ask permission and then tell them no. - if( &it == &p.weapon ) { + if( &it == &p.get_weapon() ) { if( !query_yn( _( "You are wielding that, are you sure?" ) ) ) { return false; } @@ -1638,28 +1641,28 @@ bool salvage_actor::try_to_cut_up( player &p, item &it ) const // function returns charges from it during the cutting process of the *cut. // it cuts // cut gets cut -int salvage_actor::cut_up( player &p, item &it, item_location &cut ) const +int salvage_actor::cut_up( player &p, item &it, item &cut ) const { - const bool filthy = cut.get_item()->is_filthy(); + const bool filthy = cut.is_filthy(); // total number of raw components == total volume of item. // This can go awry if there is a volume / recipe mismatch. - int count = cut.get_item()->volume() / minimal_volume_to_cut; + int count = cut.volume() / minimal_volume_to_cut; // Chance of us losing a material component to entropy. /** @EFFECT_FABRICATION reduces chance of losing components when cutting items up */ int entropy_threshold = std::max( 5, 10 - p.get_skill_level( skill_fabrication ) ); // What material components can we get back? - std::vector cut_material_components = cut.get_item()->made_of(); + std::vector cut_material_components = cut.made_of(); // What materials do we salvage (ids and counts). std::map materials_salvaged; // Final just in case check (that perhaps was not done elsewhere); - if( cut.get_item() == &it ) { + if( &cut == &it ) { add_msg( m_info, _( "You can not cut the %s with itself." ), it.tname() ); return 0; } - if( !cut.get_item()->contents.empty() ) { + if( !cut.contents.empty() ) { // Should have been ensured by try_to_cut_up - debugmsg( "tried to cut a non-empty item %s", cut.get_item()->tname() ); + debugmsg( "tried to cut a non-empty item %s", cut.tname() ); return 0; } @@ -1680,8 +1683,8 @@ int salvage_actor::cut_up( player &p, item &it, item_location &cut ) const // If more than 1 material component can still be salvaged, // chance of losing more components if the item is damaged. // If the item being cut is not damaged, no additional losses will be incurred. - if( count > 0 && cut.get_item()->damage() > 0 ) { - float component_success_chance = std::min( std::pow( 0.8, cut.get_item()->damage_level( 4 ) ), + if( count > 0 && cut.damage() > 0 ) { + float component_success_chance = std::min( std::pow( 0.8, cut.damage_level( 4 ) ), 1.0 ); for( int i = count; i > 0; i-- ) { if( component_success_chance < rng_float( 0, 1 ) ) { @@ -1699,15 +1702,16 @@ int salvage_actor::cut_up( player &p, item &it, item_location &cut ) const } add_msg( m_info, _( "You try to salvage materials from the %s." ), - cut.get_item()->tname() ); + cut.tname() ); - item_location::type cut_type = cut.where(); + item::item_location_type cut_type = cut.where(); tripoint pos = cut.position(); // Clean up before removing the item. - remove_ammo( *cut.get_item(), p ); + remove_ammo( cut, p ); // Original item has been consumed. - cut.remove_item(); + cut.detach(); + cut.destroy(); // Force an encumbrance update in case they were wearing that item. p.reset_encumbrance(); @@ -1715,22 +1719,24 @@ int salvage_actor::cut_up( player &p, item &it, item_location &cut ) const for( const auto &salvaged : materials_salvaged ) { itype_id mat_name = salvaged.first; int amount = salvaged.second; - item result( mat_name, calendar::turn ); + item &result = *item::spawn( mat_name, calendar::turn ); if( amount > 0 ) { add_msg( m_good, vgettext( "Salvaged %1$i %2$s.", "Salvaged %1$i %2$s.", amount ), amount, result.display_name( amount ) ); if( filthy ) { result.set_flag( "FILTHY" ); } - if( cut_type == item_location::type::character ) { + if( cut_type == item::item_location_type::character ) { p.i_add_or_drop( result, amount ); } else { for( int i = 0; i < amount; i++ ) { - here.spawn_an_item( pos.xy(), result, amount, 0 ); + here.spawn_an_item( pos.xy(), *item::spawn( result ), amount, 0 ); } + result.destroy(); } } else { add_msg( m_bad, _( "Could not salvage a %s." ), result.display_name() ); + result.destroy(); } } // No matter what, cutting has been done by the time we get here. @@ -1866,7 +1872,7 @@ int inscribe_actor::use( player &p, item &it, bool t, const tripoint & ) const dest_.value() ); } - item_location loc = game_menus::inv::titled_menu( get_avatar(), _( "Inscribe which item?" ) ); + item* loc = game_menus::inv::titled_menu( get_avatar(), _( "Inscribe which item?" ) ); if( !loc ) { p.add_msg_if_player( m_info, _( "Never mind." ) ); return 0; @@ -2026,12 +2032,12 @@ int enzlave_actor::use( player &p, item &it, bool t, const tripoint & ) const map_stack items = get_map().i_at( point( p.posx(), p.posy() ) ); std::vector corpses; - for( item &corpse_candidate : items ) { - const mtype *mt = corpse_candidate.get_mtype(); - if( corpse_candidate.is_corpse() && mt->in_species( ZOMBIE ) && + for( item * const &corpse_candidate : items ) { + const mtype *mt = corpse_candidate->get_mtype(); + if( corpse_candidate->is_corpse() && mt->in_species( ZOMBIE ) && mt->made_of( material_id( "flesh" ) ) && - mt->in_species( HUMAN ) && corpse_candidate.active && !corpse_candidate.has_var( "zlave" ) ) { - corpses.push_back( &corpse_candidate ); + mt->in_species( HUMAN ) && corpse_candidate->active && !corpse_candidate->has_var( "zlave" ) ) { + corpses.push_back( corpse_candidate ); } } @@ -2730,7 +2736,7 @@ int holster_actor::use( player &p, item &it, bool, const tripoint & ) const pos = -1; } - std::list top_contents{ it.contents.all_items_top() }; + std::vector top_contents{ it.contents.all_items_top() }; std::transform( top_contents.begin(), top_contents.end(), std::back_inserter( opts ), []( const item * elem ) { return string_format( _( "Draw %s" ), elem->display_name() ); @@ -2767,14 +2773,15 @@ int holster_actor::use( player &p, item &it, bool, const tripoint & ) const } } else { - item_location loc = game_menus::inv::holster( p, it ); + item* loc = game_menus::inv::holster( p, it ); if( !loc ) { p.add_msg_if_player( _( "Never mind." ) ); return 0; } - - store( p, it, *loc.obtain( p ) ); + //TODO!: check this + loc->obtain(p); + store( p, it, *loc ); } return 0; @@ -2878,8 +2885,8 @@ bool bandolier_actor::reload( player &p, item &obj ) const // convert these into reload options and display the selection prompt std::vector opts; std::transform( std::make_move_iterator( found.begin() ), std::make_move_iterator( found.end() ), - std::back_inserter( opts ), [&]( item_location && e ) { - return item::reload_option( &p, &obj, &obj, e ); + std::back_inserter( opts ), [&]( item * e ) { + return item::reload_option( &p, &obj, &obj, *e ); } ); item::reload_option sel = p.select_ammo( obj, std::move( opts ) ); @@ -2891,19 +2898,21 @@ bool bandolier_actor::reload( player &p, item &obj ) const // add or stack the ammo dependent upon existing contents if( obj.contents.empty() ) { - item put = sel.ammo->split( sel.qty() ); + item &put = sel.ammo->split( sel.qty() ); if( !put.is_null() ) { obj.put_in( put ); } else { + //TODO!: check + sel.ammo->detach(); obj.put_in( *sel.ammo ); - sel.ammo.remove_item(); } } else { obj.contents.front().charges += sel.qty(); if( sel.ammo->charges > sel.qty() ) { sel.ammo->charges -= sel.qty(); } else { - sel.ammo.remove_item(); + //TODO!: Check this one + sel.ammo->detach(); } } @@ -2986,19 +2995,23 @@ void ammobelt_actor::info( const item &, std::vector &dump ) const int ammobelt_actor::use( player &p, item &, bool, const tripoint & ) const { - item mag( belt ); + item &mag = *item::spawn( belt ); mag.ammo_unset(); if( !p.can_reload( mag ) ) { p.add_msg_if_player( _( "Insufficient ammunition to assemble %s" ), mag.tname() ); + mag.destroy(); return 0; } item::reload_option opt = p.select_ammo( mag, true ); if( opt ) { p.assign_activity( ACT_RELOAD, opt.moves(), opt.qty() ); - p.activity.targets.emplace_back( p, &p.i_add( mag ) ); + p.i_add(mag); + p.activity.targets.emplace_back( &mag ); p.activity.targets.push_back( std::move( opt.ammo ) ); + } else { + mag.destroy(); } return 0; @@ -3057,11 +3070,11 @@ bool repair_item_actor::can_use_tool( const player &p, const item &tool, bool pr return true; } -static item_location get_item_location( player &p, item &it, const tripoint &pos ) +static item* get_item_location( player &p, item &it, const tripoint &pos ) { // Item on a character if( p.has_item( it ) ) { - return item_location( p, &it ); + return ⁢ } // Item in a vehicle @@ -3076,12 +3089,12 @@ static item_location get_item_location( player &p, item &it, const tripoint &pos return VisitResponse::NEXT; } ); if( found_in_vehicle ) { - return item_location( vc, &it ); + return ⁢ } } // Item on the map - return item_location( pos, &it ); + return ⁢ } int repair_item_actor::use( player &p, item &it, bool, const tripoint &position ) const @@ -3413,7 +3426,7 @@ repair_item_actor::repair_type repair_item_actor::default_action( const item &fi return RT_NOTHING; } -static bool damage_item( player &pl, item_location &fix ) +static bool damage_item( player &pl, item*fix ) { const std::string startdurability = fix->durability_indicator( true ); const auto destroyed = fix->inc_damage(); @@ -3422,13 +3435,14 @@ static bool damage_item( player &pl, item_location &fix ) startdurability, resultdurability ); if( destroyed ) { pl.add_msg_if_player( m_bad, _( "You destroy it!" ) ); - if( fix.where() == item_location::type::character ) { - pl.i_rem_keep_contents( pl.get_item_position( fix.get_item() ) ); + if( fix->where() == item::item_location_type::character ) { + pl.i_rem_keep_contents( pl.get_item_position( fix ) ); } else { - for( const item *it : fix->contents.all_items_top() ) { - put_into_vehicle_or_drop( pl, item_drop_reason::deliberate, { *it }, fix.position() ); - } - fix.remove_item(); + //for( const item *it : fix->contents.all_items_top() ) { + put_into_vehicle_or_drop( pl, item_drop_reason::deliberate, fix->contents.all_items_top(), + fix->position() ); + //} + fix->detach(); } return true; @@ -3438,19 +3452,19 @@ static bool damage_item( player &pl, item_location &fix ) } repair_item_actor::attempt_hint repair_item_actor::repair( player &pl, item &tool, - item_location &fix ) const + item& fix ) const { if( !can_use_tool( pl, tool, true ) ) { return AS_CANT_USE_TOOL; } - if( !can_repair_target( pl, *fix, true ) ) { + if( !can_repair_target( pl, fix, true ) ) { return AS_CANT; } const int current_skill_level = pl.get_skill_level( used_skill ); - const auto action = default_action( *fix, current_skill_level ); - const auto chance = repair_chance( pl, *fix, action ); - int practice_amount = repair_recipe_difficulty( pl, *fix, true ) / 2 + 1; + const auto action = default_action( fix, current_skill_level ); + const auto chance = repair_chance( pl, fix, action ); + int practice_amount = repair_recipe_difficulty( pl, fix, true ) / 2 + 1; float roll_value = rng_float( 0.0, 1.0 ); enum roll_result { SUCCESS, @@ -3478,7 +3492,7 @@ repair_item_actor::attempt_hint repair_item_actor::repair( player &pl, item &too pl.practice( used_skill, practice_amount, trains_skill_to ); if( roll == FAILURE ) { - return damage_item( pl, fix ) ? AS_DESTROYED : AS_FAILURE; + return damage_item( pl, &fix ) ? AS_DESTROYED : AS_FAILURE; } if( action == RT_PRACTICE ) { @@ -3487,16 +3501,16 @@ repair_item_actor::attempt_hint repair_item_actor::repair( player &pl, item &too if( action == RT_REPAIR ) { if( roll == SUCCESS ) { - const std::string startdurability = fix->durability_indicator( true ); - const auto damage = fix->damage(); - handle_components( pl, *fix, false, false ); - fix->set_damage( std::max( damage - itype::damage_scale, 0 ) ); - const std::string resultdurability = fix->durability_indicator( true ); + const std::string startdurability = fix.durability_indicator( true ); + const auto damage = fix.damage(); + handle_components( pl, fix, false, false ); + fix.set_damage( std::max( damage - itype::damage_scale, 0 ) ); + const std::string resultdurability = fix.durability_indicator( true ); if( damage > itype::damage_scale ) { - pl.add_msg_if_player( m_good, _( "You repair your %s! ( %s-> %s)" ), fix->tname( 1, false ), + pl.add_msg_if_player( m_good, _( "You repair your %s! ( %s-> %s)" ), fix.tname( 1, false ), startdurability, resultdurability ); } else { - pl.add_msg_if_player( m_good, _( "You repair your %s completely! ( %s-> %s)" ), fix->tname( 1, + pl.add_msg_if_player( m_good, _( "You repair your %s completely! ( %s-> %s)" ), fix.tname( 1, false ), startdurability, resultdurability ); } return AS_SUCCESS; @@ -3507,12 +3521,12 @@ repair_item_actor::attempt_hint repair_item_actor::repair( player &pl, item &too if( action == RT_REFIT ) { if( roll == SUCCESS ) { - if( !fix->has_flag( "FIT" ) ) { + if( !fix.has_flag( "FIT" ) ) { pl.add_msg_if_player( m_good, _( "You take your %s in, improving the fit." ), - fix->tname() ); - fix->set_flag( "FIT" ); + fix.tname() ); + fix.set_flag( "FIT" ); } - handle_components( pl, *fix, false, false ); + handle_components( pl, fix, false, false ); return AS_SUCCESS; } @@ -3523,9 +3537,9 @@ repair_item_actor::attempt_hint repair_item_actor::repair( player &pl, item &too //We don't need to check for smallness or undersize because DOWNSIZING already guarantees that if( roll == SUCCESS ) { pl.add_msg_if_player( m_good, _( "You resize the %s to accommodate your tiny build." ), - fix->tname().c_str() ); - fix->set_flag( "UNDERSIZE" ); - handle_components( pl, *fix, false, false ); + fix.tname().c_str() ); + fix.set_flag( "UNDERSIZE" ); + handle_components( pl, fix, false, false ); return AS_SUCCESS; } return AS_RETRY; @@ -3535,32 +3549,32 @@ repair_item_actor::attempt_hint repair_item_actor::repair( player &pl, item &too //We don't need to check for smallness or undersize because UPSIZING already guarantees that if( roll == SUCCESS ) { pl.add_msg_if_player( m_good, _( "You adjust the %s back to its normal size." ), - fix->tname().c_str() ); - fix->unset_flag( "UNDERSIZE" ); - handle_components( pl, *fix, false, false ); + fix.tname().c_str() ); + fix.unset_flag( "UNDERSIZE" ); + handle_components( pl, fix, false, false ); return AS_SUCCESS; } return AS_RETRY; } if( action == RT_REINFORCE ) { - if( fix->has_flag( "PRIMITIVE_RANGED_WEAPON" ) || !fix->reinforceable() ) { + if( fix.has_flag( "PRIMITIVE_RANGED_WEAPON" ) || !fix.reinforceable() ) { pl.add_msg_if_player( m_info, _( "You cannot improve your %s any more this way." ), - fix->tname() ); + fix.tname() ); return AS_CANT; } if( roll == SUCCESS ) { - pl.add_msg_if_player( m_good, _( "You make your %s extra sturdy." ), fix->tname() ); - fix->mod_damage( -itype::damage_scale ); - handle_components( pl, *fix, false, false ); + pl.add_msg_if_player( m_good, _( "You make your %s extra sturdy." ), fix.tname() ); + fix.mod_damage( -itype::damage_scale ); + handle_components( pl, fix, false, false ); return AS_SUCCESS; } return AS_RETRY; } - pl.add_msg_if_player( m_info, _( "Your %s is already enhanced." ), fix->tname() ); + pl.add_msg_if_player( m_info, _( "Your %s is already enhanced." ), fix.tname() ); return AS_CANT; } @@ -3682,7 +3696,7 @@ int heal_actor::use( player &p, item &it, bool, const tripoint &pos ) const // Assign first aid long action. /** @EFFECT_FIRSTAID speeds up firstaid activity */ p.assign_activity( ACT_FIRSTAID, cost, 0, 0, it.tname() ); - p.activity.targets.push_back( item_location( p, &it ) ); + p.activity.targets.push_back( &it ); p.activity.values.push_back( hpp ); p.moves = 0; return 0; @@ -3829,7 +3843,7 @@ int heal_actor::finish_using( player &healer, player &patient, item &it, hp_part it.set_flag( flag ); } } else { - item used_up( used_up_item_id, it.birthday() ); + item &used_up = *item::spawn( used_up_item_id, it.birthday() ); used_up.charges = used_up_item_charges; for( const auto &flag : used_up_item_flags ) { used_up.set_flag( flag ); @@ -4267,9 +4281,9 @@ int saw_barrel_actor::use( player &p, item &it, bool t, const tripoint & ) const return 0; } - item &obj = *loc.obtain( p ); - p.add_msg_if_player( _( "You saw down the barrel of your %s." ), obj.tname() ); - obj.put_in( item( "barrel_small", calendar::turn ) ); + loc->obtain( p ); + p.add_msg_if_player( _( "You saw down the barrel of your %s." ), loc->tname() ); + loc->put_in( *item::spawn( "barrel_small", calendar::turn ) ); return 0; } @@ -4741,7 +4755,7 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const itm.made_of_any( materials ); }; // note: if !p.is_npc() then p is avatar. - item_location loc = game_menus::inv::titled_filter_menu( + item* loc = game_menus::inv::titled_filter_menu( filter, *p.as_avatar(), _( "Enhance which clothing?" ) ); if( !loc ) { p.add_msg_if_player( m_info, _( "You do not have that item!" ) ); @@ -4756,11 +4770,11 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const // Gives us an item with the mod added or removed (toggled) const auto modded_copy = []( const item & proto, const std::string & mod_type ) { - item mcopy = proto; - if( mcopy.has_own_flag( mod_type ) == 0 ) { - mcopy.set_flag( mod_type ); + item *mcopy = item::spawn_temporary( proto ); + if( mcopy->has_own_flag( mod_type ) == 0 ) { + mcopy->set_flag( mod_type ); } else { - mcopy.unset_flag( mod_type ); + mcopy->unset_flag( mod_type ); } return mcopy; @@ -4811,7 +4825,7 @@ int sew_advanced_actor::use( player &p, item &it, bool, const tripoint & ) const int index = 0; for( auto cm : clothing_mods ) { auto obj = cm.obj(); - item temp_item = modded_copy( mod, obj.flag ); + item &temp_item = *modded_copy( mod, obj.flag ); temp_item.update_clothing_mod_val(); bool enab = false; diff --git a/src/iuse_actor.h b/src/iuse_actor.h index f8aa5603e319..a40de229283b 100644 --- a/src/iuse_actor.h +++ b/src/iuse_actor.h @@ -34,7 +34,6 @@ struct tripoint; enum hp_part : int; enum body_part : int; class JsonObject; -class item_location; struct furn_t; struct itype; @@ -592,7 +591,7 @@ class salvage_actor : public iuse_actor }; bool try_to_cut_up( player &p, item &it ) const; - int cut_up( player &p, item &it, item_location &cut ) const; + int cut_up( player &p, item &it, item &cut ) const; int time_to_cut_up( const item &it ) const; bool valid_to_cut_up( const item &it ) const; @@ -971,7 +970,7 @@ class repair_item_actor : public iuse_actor }; /** Attempts to repair target item with selected tool */ - attempt_hint repair( player &pl, item &tool, item_location &fix ) const; + attempt_hint repair( player &pl, item &tool, item &fix ) const; /** Checks if repairs on target item are possible. Excludes checks on tool. * Doesn't just estimate - should not return true if repairs are not possible or false if they are. */ bool can_repair_target( player &pl, const item &fix, bool print_msg ) const; diff --git a/src/json.h b/src/json.h index 589d2323a150..3b6bceb664a8 100644 --- a/src/json.h +++ b/src/json.h @@ -18,6 +18,8 @@ #include "enum_conversions.h" #include "memory_fast.h" #include "string_id.h" +#include "colony.h" +#include "safe_reference.h" /* Cataclysm-DDA homegrown JSON tools * copyright CC-BY-SA-3.0 2013 CleverRaven @@ -43,8 +45,6 @@ namespace cata { template class optional; -template -class colony; } // namespace cata class JsonError : public std::runtime_error @@ -351,6 +351,32 @@ class JsonIn return false; } + //TODO!: not sure this is correct, needs a proper check + /// Overload for game objects + template + auto read( T *&out, bool throw_on_error = false ) -> decltype( T::spawn( *this ), true ) { + try { + out = T::spawn( *this ); + return true; + } catch( const JsonError & ) { + if( throw_on_error ) { + throw; + } + return false; + } + } + + /// Overload for safe references + template + bool read( safe_reference &out, bool throw_on_error = false ) { + uint64_t id; + if( !read( id, throw_on_error ) ) { + return false; + } + out = std::move( safe_reference( id ) ); + return true; + } + /// Overload for std::pair template bool read( std::pair &p, bool throw_on_error = false ) { @@ -469,8 +495,8 @@ class JsonIn // special case for colony as it uses `insert()` instead of `push_back()` // and therefore doesn't fit with vector/deque/list - template - bool read( cata::colony &v, bool throw_on_error = false ) { + /*template + bool read( cata::colony &v, bool throw_on_error = false ) { if( !test_array() ) { return error_or_false( throw_on_error, "Expected json array" ); } @@ -493,7 +519,7 @@ class JsonIn } return true; - } + }*/ // object ~> containers with unmatching key_type and value_type // map, unordered_map ~> object @@ -637,11 +663,30 @@ class JsonOut v.serialize( *this ); } + + /// Overload that dereferences before calling a global function, for use with game objects + template + auto write( const T *const &v ) -> decltype( serialize( *v, *this ), void() ) { + serialize( *v, *this ); + } + + /// Overload that dereferences before calling a member function, for use with game objects + template + auto write( const T *const &v ) -> decltype( v->serialize( *this ), void() ) { + v->serialize( *this ); + } + + template ::value, int>::type = 0> void write( T val ) { write( static_cast::type>( val ) ); } + template + void write( const safe_reference &val ) { + write( val.serialize() ); + } + // strings need escaping and quoting void write( const std::string &val ); void write( const char *val ) { @@ -720,8 +765,8 @@ class JsonOut } // special case for colony, since it doesn't fit in other categories - template - void write( const cata::colony &container ) { + template + void write( const cata::colony &container ) { write_as_array( container ); } diff --git a/src/lightmap.cpp b/src/lightmap.cpp index de6f7a49c60a..41f77f973e97 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -68,7 +68,7 @@ void map::add_light_from_items( const tripoint &p, item_stack::iterator begin, float ilum = 0.0f; // brightness units::angle iwidth = 0_degrees; // 0-360 degrees. 0 is a circular light_source units::angle idir = 0_degrees; // otherwise, it's a light_arc pointed in this direction - if( itm_it->getlight( ilum, iwidth, idir ) ) { + if( ( *itm_it )->getlight( ilum, iwidth, idir ) ) { if( iwidth > 0_degrees ) { apply_light_arc( p, idir, ilum, iwidth ); } else { diff --git a/src/locations.cpp b/src/locations.cpp new file mode 100644 index 000000000000..1af50c61901e --- /dev/null +++ b/src/locations.cpp @@ -0,0 +1,31 @@ +#include "locations.h" + +#include "item.h" +#include "character.h" +#include "submap.h" + +void wield_item_location::release() +{ + holder->remove_weapon(); +} + +void template_item_location::release() +{ + debugmsg( "Attempted to release a template item" ); +} + +bool template_item_location::is_loaded() +{ + return false; //Loaded means in the reality bubble so no +} + +void template_item_location::release_for_destroy() +{ + //Just suppressing the errors here, there's nothing to do to release +} + +void loc_item_location::release_for_destroy() +{ + debugmsg( "Attempted to release an item with a location" ); + release(); +} diff --git a/src/locations.h b/src/locations.h new file mode 100644 index 000000000000..079179e8687b --- /dev/null +++ b/src/locations.h @@ -0,0 +1,114 @@ +#pragma once +#ifndef CATA_SRC_LOCATION_H +#define CATA_SRC_LOCATION_H + +#include "point.h" + +class item; +class Character; +class submap; +class vehicle; +class monster; +struct tripoint; + +class loc_item_location +{ + public: + virtual void release() = 0; + virtual void release_for_destroy(); + virtual bool is_loaded() = 0; + virtual ~loc_item_location(); +}; + +class character_inventory_item_location : public loc_item_location +{ + Character *holder; + + void release() override; + bool is_loaded() override; +}; + +class wield_item_location : public loc_item_location +{ + Character *holder; + void release() override; + bool is_loaded() override; +}; + +class worn_item_location : public loc_item_location +{ + Character *wearer; + void release() override; + bool is_loaded() override; +}; + +class tile_item_location : public loc_item_location +{ + tripoint loc; + submap *sm; + void release() override; + bool is_loaded() override; +}; + +class tied_item_location : public loc_item_location +{ + monster *on; + void release() override; + bool is_loaded() override; +}; +class tack_item_location : public loc_item_location +{ + monster *on; + void release() override; + bool is_loaded() override; +}; +class monster_armor_item_location : public loc_item_location +{ + monster *on; + void release() override; + bool is_loaded() override; +}; +class monster_storage_item_location : public loc_item_location +{ + monster *on; + void release() override; + bool is_loaded() override; +}; + +class monster_battery_item_location : public loc_item_location +{ + monster *on; + void release() override; + bool is_loaded() override; +}; + +class vehicle_base_item_location : public loc_item_location +{ + vehicle *veh; + void release() override; + bool is_loaded() override; +}; + +class vehicle_item_location : public loc_item_location +{ + vehicle *veh; + point mount;//TODO!: part ref rather than mount maybe? + void release() override; + bool is_loaded() override; +}; + +class contents_item_location : public loc_item_location +{ + item *container; + void release() override; + bool is_loaded() override; +}; + +class template_item_location : public loc_item_location +{ + void release() override; + bool is_loaded() override; + void release_for_destroy() override; +}; + +#endif diff --git a/src/magic_spell_effect.cpp b/src/magic_spell_effect.cpp index 891dabb02cb1..92176c5c86e1 100644 --- a/src/magic_spell_effect.cpp +++ b/src/magic_spell_effect.cpp @@ -665,8 +665,9 @@ static void spell_move( const spell &sp, const Creature &caster, if( sp.is_valid_effect_target( target_item ) ) { auto src_items = here.i_at( from ); auto dst_items = here.i_at( to ); - for( const item &item : src_items ) { - dst_items.insert( item ); + //TODO!: check + for( item *&item : src_items ) { + dst_items.insert( *item ); } src_items.clear(); } @@ -727,7 +728,7 @@ void spell_effect::area_push( const spell &sp, Creature &caster, const tripoint void spell_effect::spawn_ethereal_item( const spell &sp, Creature &caster, const tripoint & ) { - item granted( sp.effect_data(), calendar::turn ); + item &granted = *item::spawn( sp.effect_data(), calendar::turn ); if( !granted.is_comestible() && !( sp.has_flag( spell_flag::PERMANENT ) && sp.is_max_level() ) ) { granted.set_var( "ethereal", to_turns( sp.duration_turns() ) ); granted.set_flag( "ETHEREAL_ITEM" ); @@ -740,7 +741,7 @@ void spell_effect::spawn_ethereal_item( const spell &sp, Creature &caster, const granted.set_flag( "FIT" ); you.wear_item( granted, false ); } else if( !you.is_armed() ) { - you.weapon = granted; + you.set_weapon(granted); } else { you.i_add( granted ); } diff --git a/src/map.cpp b/src/map.cpp index fcce05702060..154a6d904720 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -137,7 +137,7 @@ static const ter_str_id t_rock_floor_no_roof( "t_rock_floor_no_roof" ); #define dbg(x) DebugLog((x),DC::Map) -static cata::colony nulitems; // Returned when &i_at() is asked for an OOB value +static cata::colony nulitems; // Returned when &i_at() is asked for an OOB value static field nulfield; // Returned when &field_at() is asked for an OOB value static level_cache nullcache; // Dummy cache for z-levels outside bounds @@ -154,7 +154,7 @@ map_stack::iterator map_stack::erase( map_stack::const_iterator it ) return myorigin->i_rem( location, it ); } -void map_stack::insert( const item &newitem ) +void map_stack::insert( item &newitem ) { myorigin->add_item_or_charges( location, newitem ); } @@ -1472,14 +1472,15 @@ std::string map::furnname( const tripoint &p ) if( f.has_flag( "PLANT" ) ) { // Can't use item_stack::only_item() since there might be fertilizer map_stack items = i_at( p ); - const map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.is_seed(); + const map_stack::iterator seed = std::find_if( items.begin(), + items.end(), []( const item * const & it ) { + return it->is_seed(); } ); if( seed == items.end() ) { debugmsg( "Missing seed for plant at (%d, %d, %d)", p.x, p.y, p.z ); return "null"; } - const std::string &plant = seed->get_plant_name(); + const std::string &plant = ( *seed )->get_plant_name(); return string_format( "%s (%s)", f.name(), plant ); } else { return f.name(); @@ -2130,8 +2131,10 @@ void map::drop_furniture( const tripoint &p ) if( frn_obj.has_flag( TFLAG_SEALED ) && has_items( p ) ) { auto old_items = i_at( p ); auto new_items = i_at( current ); + + //TODO!:checkkkk for( const auto &it : old_items ) { - new_items.insert( it ); + new_items.insert( *it ); } i_clear( p ); @@ -2222,7 +2225,8 @@ void map::drop_items( const tripoint &p ) for( const auto &i : items ) { // TODO: Bash the item up before adding it // TODO: Bash the creature, terrain, furniture and vehicles on the tile - add_item_or_charges( below, i ); + //TODO!: checkky + add_item_or_charges( below, *i ); } // Just to make a sound for now @@ -2535,27 +2539,27 @@ void map::make_rubble( const tripoint &p, const furn_id &rubble_type, const bool //Still hardcoded, but a step up from the old stuff due to being in only one place if( rubble_type == f_wreckage ) { - item chunk( "steel_chunk", calendar::turn ); - item scrap( "scrap", calendar::turn ); - add_item_or_charges( p, chunk ); - add_item_or_charges( p, scrap ); + item *chunk = item::spawn( "steel_chunk", calendar::turn ); + item *scrap = item::spawn( "scrap", calendar::turn ); + add_item_or_charges( p, *chunk ); + add_item_or_charges( p, *scrap ); if( one_in( 5 ) ) { - item pipe( "pipe", calendar::turn ); - item wire( "wire", calendar::turn ); - add_item_or_charges( p, pipe ); - add_item_or_charges( p, wire ); + item *pipe = item::spawn( "pipe", calendar::turn ); + item *wire = item::spawn( "wire", calendar::turn ); + add_item_or_charges( p, *pipe ); + add_item_or_charges( p, *wire ); } } else if( rubble_type == f_rubble_rock ) { - item rock( "rock", calendar::turn ); int rock_count = rng( 1, 3 ); for( int i = 0; i < rock_count; i++ ) { - add_item_or_charges( p, rock ); + item *rock = item::spawn( "rock", calendar::turn ); + add_item_or_charges( p, *rock ); } } else if( rubble_type == f_rubble ) { - item splinter( "splinter", calendar::turn ); int splinter_count = rng( 2, 8 ); for( int i = 0; i < splinter_count; i++ ) { - add_item_or_charges( p, splinter ); + item *splinter = item::spawn( "splinter", calendar::turn ); + add_item_or_charges( p, *splinter ); } spawn_item( p, itype_nail, 1, rng( 20, 50 ) ); } @@ -2626,7 +2630,7 @@ bool map::is_last_ter_wall( const bool no_furn, const point &p, bool map::tinder_at( const tripoint &p ) { for( const auto &i : i_at( p ) ) { - if( i.has_flag( "TINDER" ) ) { + if( i->has_flag( "TINDER" ) ) { return true; } } @@ -2642,7 +2646,7 @@ bool map::flammable_items_at( const tripoint &p, int threshold ) } for( const auto &i : i_at( p ) ) { - if( i.flammable( threshold ) ) { + if( i->flammable( threshold ) ) { return true; } } @@ -2808,8 +2812,8 @@ bool map::mop_spills( const tripoint &p ) if( !has_flag( "LIQUIDCONT", p ) && !has_flag( "SEALED", p ) ) { auto items = i_at( p ); - auto new_end = std::remove_if( items.begin(), items.end(), []( const item & it ) { - return it.made_of( LIQUID ); + auto new_end = std::remove_if( items.begin(), items.end(), []( const item * const & it ) { + return it->made_of( LIQUID ); } ); retval = new_end != items.end(); while( new_end != items.end() ) { @@ -2845,8 +2849,8 @@ bool map::mop_spills( const tripoint &p ) } //remove any liquids that somehow didn't fall through to the ground vehicle_stack here = veh->get_items( elem ); - auto new_end = std::remove_if( here.begin(), here.end(), []( const item & it ) { - return it.made_of( LIQUID ); + auto new_end = std::remove_if( here.begin(), here.end(), []( const item * const & it ) { + return it->made_of( LIQUID ); } ); retval |= ( new_end != here.end() ); while( new_end != here.end() ) { @@ -3006,19 +3010,20 @@ void map::smash_items( const tripoint &p, const int power, const std::string &ca // TODO: Bullets should be pretty much corpse-only constexpr const int min_destroy_threshold = 50; - std::vector contents; + std::vector contents; map_stack items = i_at( p ); - for( auto i = items.begin(); i != items.end(); ) { + for( auto it = items.begin(); it != items.end(); ) { + item *i = *it; // If the power is low or it's not an explosion, only pulp rezing corpses if( ( power < min_destroy_threshold || !do_destroy ) && !i->can_revive() ) { - i++; + it++; continue; } // Active explosives arbitrarily get double the destroy threshold bool is_active_explosive = i->active && i->type->get_use( "explosion" ) != nullptr; if( is_active_explosive && i->charges == 0 ) { - i++; + it++; continue; } @@ -3069,18 +3074,21 @@ void map::smash_items( const tripoint &p, const int power, const std::string &ca // But save the contents, except for irremovable gunmods for( item *elem : i->contents.all_items_top() ) { if( !elem->is_irremovable() ) { - contents.push_back( item( *elem ) ); + //TODO!: check + contents.push_back( item::spawn( *elem ) ); } } if( items_damaged == 0 ) { damaged_item_name = i->tname(); } - i = i_rem( p, i ); + //TODO!: CHECJKEKCEK, need to destroy the item + it = items.erase( it ); + i->destroy(); items_damaged++; items_destroyed++; } else { - i++; + it++; } } @@ -3097,8 +3105,9 @@ void map::smash_items( const tripoint &p, const int power, const std::string &ca add_msg( m_bad, _( "The %1$s damages the %2$s." ), cause_message, damaged_item_name ); } - for( const item &it : contents ) { - add_item_or_charges( p, it ); + //TODO!: check + for( item *&it : contents ) { + add_item_or_charges( p, *it ); } } @@ -3370,8 +3379,9 @@ bash_results map::bash_furn_success( const tripoint &p, const bash_params ¶m soundfxvariant = "smash_cloth"; } else { furn_set( p, bash.furn_set ); - for( item &it : i_at( p ) ) { - it.on_drop( p, *this ); + for( item * const &it : i_at( p ) ) { + //TODO!: check + it->on_drop( p, *this ); } // HACK: Hack alert. // Signs have cosmetics associated with them on the submap since @@ -3568,17 +3578,19 @@ bash_results map::bash_items( const tripoint &p, const bash_params ¶ms ) if( !has_items( p ) ) { return result; } + //TODO!: check alla this - std::vector smashed_contents; + std::vector smashed_contents; auto bashed_items = i_at( p ); bool smashed_glass = false; for( auto bashed_item = bashed_items.begin(); bashed_item != bashed_items.end(); ) { // the check for active suppresses Molotovs smashing themselves with their own explosion - if( bashed_item->made_of( material_id( "glass" ) ) && !bashed_item->active && one_in( 2 ) ) { + if( ( *bashed_item )->made_of( material_id( "glass" ) ) && !( *bashed_item )->active && + one_in( 2 ) ) { result.did_bash = true; smashed_glass = true; - for( const item *bashed_content : bashed_item->contents.all_items_top() ) { - smashed_contents.push_back( item( *bashed_content ) ); + for( item *bashed_content : ( *bashed_item )->contents.all_items_top() ) { + smashed_contents.push_back( bashed_content ); } bashed_item = bashed_items.erase( bashed_item ); } else { @@ -4106,12 +4118,12 @@ map_stack::iterator map::i_rem( const tripoint &p, map_stack::const_iterator it submap *const current_submap = get_submap_at( p, l ); // remove from the active items cache (if it isn't there does nothing) - current_submap->active_items.remove( &*it ); + current_submap->active_items.remove( *it ); if( current_submap->active_items.empty() ) { submaps_with_active_items.erase( tripoint( abs_sub.x + p.x / SEEX, abs_sub.y + p.y / SEEY, p.z ) ); } - current_submap->update_lum_rem( l, *it ); + current_submap->update_lum_rem( l, **it ); return current_submap->get_items( l ).erase( it ); } @@ -4130,9 +4142,9 @@ void map::i_clear( const tripoint &p ) point l; submap *const current_submap = get_submap_at( p, l ); - for( item &it : current_submap->get_items( l ) ) { + for( item * const &it : current_submap->get_items( l ) ) { // remove from the active items cache (if it isn't there does nothing) - current_submap->active_items.remove( &it ); + current_submap->active_items.remove( it ); } if( current_submap->active_items.empty() ) { submaps_with_active_items.erase( tripoint( abs_sub.x + p.x / SEEX, abs_sub.y + p.y / SEEY, p.z ) ); @@ -4142,37 +4154,38 @@ void map::i_clear( const tripoint &p ) current_submap->get_items( l ).clear(); } -item &map::spawn_an_item( const tripoint &p, item new_item, +item &map::spawn_an_item( const tripoint &p, item &new_item, const int charges, const int damlevel ) { if( charges && new_item.charges > 0 ) { //let's fail silently if we specify charges for an item that doesn't support it new_item.charges = charges; } - new_item = new_item.in_its_container(); - if( ( new_item.made_of( LIQUID ) && has_flag( "SWIMMABLE", p ) ) || + item *spawned_item = &new_item.in_its_container(); + if( ( spawned_item->made_of( LIQUID ) && has_flag( "SWIMMABLE", p ) ) || has_flag( "DESTROY_ITEM", p ) ) { + spawned_item->destroy(); return null_item_reference(); } - new_item.set_damage( damlevel ); + spawned_item->set_damage( damlevel ); - return add_item_or_charges( p, new_item ); + return add_item_or_charges( p, *spawned_item ); } -std::vector map::spawn_items( const tripoint &p, const std::vector &new_items ) +std::vector map::spawn_items( const tripoint &p, std::vector new_items ) { std::vector ret; if( !inbounds( p ) || has_flag( "DESTROY_ITEM", p ) ) { return ret; } const bool swimmable = has_flag( "SWIMMABLE", p ); - for( const item &new_item : new_items ) { + for( item *&new_item : new_items ) { - if( new_item.made_of( LIQUID ) && swimmable ) { + if( new_item->made_of( LIQUID ) && swimmable ) { continue; } - item &it = add_item_or_charges( p, new_item ); + item &it = add_item_or_charges( p, *new_item ); if( !it.is_null() ) { ret.push_back( &it ); } @@ -4183,12 +4196,13 @@ std::vector map::spawn_items( const tripoint &p, const std::vector void map::spawn_artifact( const tripoint &p ) { - add_item_or_charges( p, item( new_artifact(), calendar::start_of_cataclysm ) ); + add_item_or_charges( p, *item::spawn( new_artifact(), calendar::start_of_cataclysm ) ); } void map::spawn_natural_artifact( const tripoint &p, artifact_natural_property prop ) { - add_item_or_charges( p, item( new_natural_artifact( prop ), calendar::start_of_cataclysm ) ); + add_item_or_charges( p, *item::spawn( new_natural_artifact( prop ), + calendar::start_of_cataclysm ) ); } void map::spawn_item( const tripoint &p, const itype_id &type_id, @@ -4207,7 +4221,7 @@ void map::spawn_item( const tripoint &p, const itype_id &type_id, spawn_item( p, type_id, 1, charges, birthday, damlevel ); } // spawn the item - item new_item( type_id, birthday ); + item &new_item = *item::spawn( type_id, birthday ); if( one_in( 3 ) && new_item.has_flag( "VARSIZE" ) ) { new_item.set_flag( "FIT" ); } @@ -4232,7 +4246,7 @@ units::volume map::free_volume( const tripoint &p ) return i_at( p ).free_volume(); } -item &map::add_item_or_charges( const tripoint &pos, item obj, bool overflow ) +item &map::add_item_or_charges( const tripoint &pos, item &obj, bool overflow ) { // Checks if item would not be destroyed if added to this tile auto valid_tile = [&]( const tripoint & e ) { @@ -4266,8 +4280,9 @@ item &map::add_item_or_charges( const tripoint &pos, item obj, bool overflow ) if( obj.count_by_charges() ) { for( auto &e : i_at( tile ) ) { - if( e.merge_charges( obj ) ) { - return e; + //TODO!: check + if( e->merge_charges( obj ) ) { + return *e; } } } @@ -4332,7 +4347,7 @@ item &map::add_item_or_charges( const tripoint &pos, item obj, bool overflow ) return null_item_reference(); } -item &map::add_item( const tripoint &p, item new_item ) +item &map::add_item( const tripoint &p, item &new_item ) { if( !inbounds( p ) ) { return null_item_reference(); @@ -4372,33 +4387,34 @@ item &map::add_item( const tripoint &p, item new_item ) current_submap->update_lum_add( l, new_item ); - const map_stack::iterator new_pos = current_submap->get_items( l ).insert( new_item ); + current_submap->get_items( l ).push_back( &new_item ); if( new_item.needs_processing() ) { if( current_submap->active_items.empty() ) { submaps_with_active_items.insert( tripoint( abs_sub.x + p.x / SEEX, abs_sub.y + p.y / SEEY, p.z ) ); } - current_submap->active_items.add( *new_pos, l ); + current_submap->active_items.add( new_item ); } - return *new_pos; + return new_item; } -item map::water_from( const tripoint &p ) +item &map::water_from( const tripoint &p ) { if( has_flag( "SALT_WATER", p ) ) { - return item( "salt_water", calendar::start_of_cataclysm, item::INFINITE_CHARGES ); + return *item::spawn( "salt_water", calendar::start_of_cataclysm, item::INFINITE_CHARGES ); } const ter_id terrain_id = ter( p ); if( terrain_id == t_sewage ) { - item ret( "water_sewage", calendar::start_of_cataclysm, item::INFINITE_CHARGES ); - ret.poison = rng( 1, 7 ); - return ret; + item *ret = item::spawn( "water_sewage", calendar::start_of_cataclysm, item::INFINITE_CHARGES ); + ret->poison = rng( 1, 7 ); + return *ret; } - item ret( "water", calendar::start_of_cataclysm, item::INFINITE_CHARGES ); + // iexamine::water_source requires a valid liquid from this function. if( terrain_id.obj().examine == &iexamine::water_source ) { + item *ret = item::spawn( "water", calendar::start_of_cataclysm, item::INFINITE_CHARGES ); int poison_chance = 0; if( terrain_id.obj().has_flag( TFLAG_DEEP_WATER ) ) { if( terrain_id.obj().has_flag( TFLAG_CURRENT ) ) { @@ -4414,19 +4430,20 @@ item map::water_from( const tripoint &p ) } } if( one_in( poison_chance ) ) { - ret.poison = rng( 1, 4 ); + ret->poison = rng( 1, 4 ); } - return ret; + return *ret; } if( furn( p ).obj().examine == &iexamine::water_source ) { - return ret; + item *ret = item::spawn( "water", calendar::start_of_cataclysm, item::INFINITE_CHARGES ); + return *ret; } - return item(); + return null_item_reference(); } -void map::make_active( item_location &loc ) +void map::make_active( item &loc ) { - item *target = loc.get_item(); + item *target = &loc; // Trust but verify, don't let stinking callers set items active when they shouldn't be. if( !target->needs_processing() ) { @@ -4434,19 +4451,17 @@ void map::make_active( item_location &loc ) } point l; submap *const current_submap = get_submap_at( loc.position(), l ); - cata::colony &item_stack = current_submap->get_items( l ); - cata::colony::iterator iter = item_stack.get_iterator_from_pointer( target ); if( current_submap->active_items.empty() ) { submaps_with_active_items.insert( tripoint( abs_sub.x + loc.position().x / SEEX, abs_sub.y + loc.position().y / SEEY, loc.position().z ) ); } - current_submap->active_items.add( *iter, l ); + current_submap->active_items.add( *target ); } -void map::update_lum( item_location &loc, bool add ) +void map::update_lum( item &loc, bool add ) { - item *target = loc.get_item(); + item *target = &loc; // if the item is not emissive, do nothing if( !target->is_emissive() ) { @@ -4463,14 +4478,15 @@ void map::update_lum( item_location &loc, bool add ) } } -static bool process_map_items( item_stack &items, safe_reference &item_ref, - const tripoint &location, const float insulation, const temperature_flag flag ) +static bool process_map_items( item* item_ref, const tripoint &location, const float insulation, const temperature_flag flag ) { if( item_ref->process( nullptr, location, false, insulation, flag ) ) { // Item is to be destroyed so erase it from the map stack // unless it was already destroyed by processing. + //TODO!: this needs doing right if( item_ref ) { - items.erase( items.get_iterator_from_pointer( item_ref.get() ) ); + item_ref->detach(); + item_ref->destroy(); } return true; } @@ -4488,10 +4504,10 @@ static void process_vehicle_items( vehicle &cur_veh, int part ) if( washmachine_here || dishwasher_here ) { for( auto &n : cur_veh.get_items( part ) ) { const time_duration washing_time = 90_minutes; - const time_duration time_left = washing_time - n.age(); + const time_duration time_left = washing_time - n->age(); static const std::string filthy( "FILTHY" ); if( time_left <= 0_turns ) { - n.unset_flag( filthy ); + n->unset_flag( filthy ); washing_machine_finished = true; cur_veh.part( part ).enabled = false; } else if( calendar::once_every( 15_minutes ) ) { @@ -4516,9 +4532,9 @@ static void process_vehicle_items( vehicle &cur_veh, int part ) null_part; if( recharge_part_idx >= 0 && recharge_part.enabled && !recharge_part.removed && !recharge_part.is_broken() ) { - for( item &outer : cur_veh.get_items( part ) ) { + for( item *&outer : cur_veh.get_items( part ) ) { bool out_of_battery = false; - outer.visit_items( [&cur_veh, &recharge_part, &out_of_battery]( item * it ) { + outer->visit_items( [&cur_veh, &recharge_part, &out_of_battery]( item * it ) { item &n = *it; if( !n.has_flag( flag_RECHARGE ) && !n.has_flag( flag_USE_UPS ) ) { return VisitResponse::NEXT; @@ -4610,15 +4626,15 @@ void map::process_items_in_submap( submap ¤t_submap, const tripoint &gridp // Get a COPY of the active item list for this submap. // If more are added as a side effect of processing, they are ignored this turn. // If they are destroyed before processing, they don't get processed. - std::vector active_items = current_submap.active_items.get_for_processing(); + std::vector active_items = current_submap.active_items.get_for_processing(); const point grid_offset( gridp.x * SEEX, gridp.y * SEEY ); - for( item_reference &active_item_ref : active_items ) { - if( !active_item_ref.item_ref ) { + for( item* &active_item_ref : active_items ) { + if( !active_item_ref ) { // The item was destroyed, so skip it. continue; } - const tripoint map_location = tripoint( grid_offset + active_item_ref.location, gridp.z ); + const tripoint map_location = active_item_ref->position(); // root cellars are special temperature_flag flag = temperature_flag::TEMP_NORMAL; if( ter( map_location ) == t_rootcellar ) { @@ -4631,7 +4647,7 @@ void map::process_items_in_submap( submap ¤t_submap, const tripoint &gridp flag = temperature_flag::TEMP_FREEZER; } map_stack items = i_at( map_location ); - process_map_items( items, active_item_ref.item_ref, map_location, 1, flag ); + process_map_items( active_item_ref, map_location, 1, flag ); } } @@ -4668,22 +4684,23 @@ void map::process_items_in_vehicle( vehicle &cur_veh, submap ¤t_submap ) process_vehicle_items( cur_veh, vp.part_index() ); } - for( item_reference &active_item_ref : cur_veh.active_items.get_for_processing() ) { + for( item* active_item_ref : cur_veh.active_items.get_for_processing() ) { if( empty( cargo_parts ) ) { return; - } else if( !active_item_ref.item_ref ) { + } else if( !active_item_ref ) { // The item was destroyed, so skip it. continue; } const auto it = std::find_if( begin( cargo_parts ), end( cargo_parts ), [&]( const vpart_reference & part ) { - return active_item_ref.location == part.mount(); + //TODO!: mount position? Wtf is this + return active_item_ref->position() == cur_veh.mount_to_tripoint(part.mount()); } ); if( it == end( cargo_parts ) ) { continue; // Can't find a cargo part matching the active item. } - const item &target = *active_item_ref.item_ref; + const item &target = *active_item_ref; // Find the cargo part and coordinates corresponding to the current active item. const vehicle_part &pt = it->part(); const tripoint item_loc = it->pos(); @@ -4706,7 +4723,7 @@ void map::process_items_in_vehicle( vehicle &cur_veh, submap ¤t_submap ) flag = temperature_flag::TEMP_FREEZER; } } - if( !process_map_items( items, active_item_ref.item_ref, item_loc, it_insulation, flag ) ) { + if( !process_map_items( active_item_ref, item_loc, it_insulation, flag ) ) { // If the item was NOT destroyed, we can skip the remainder, // which handles fallout from the vehicle being damaged. continue; @@ -4781,12 +4798,12 @@ bool map::has_items( const tripoint &p ) const } template -std::list use_amount_stack( Stack stack, const itype_id &type, int &quantity, - const std::function &filter ) +ItemList use_amount_stack( Stack stack, const itype_id &type, int &quantity, + const std::function &filter ) { - std::list ret; + ItemList ret; for( auto a = stack.begin(); a != stack.end() && quantity > 0; ) { - if( a->use_amount( type, quantity, ret, filter ) ) { + if( ( *a )->use_amount( type, quantity, ret, filter ) ) { a = stack.erase( a ); } else { ++a; @@ -4795,37 +4812,37 @@ std::list use_amount_stack( Stack stack, const itype_id &type, int &quanti return ret; } -std::list map::use_amount_square( const tripoint &p, const itype_id &type, - int &quantity, const std::function &filter ) +ItemList map::use_amount_square( const tripoint &p, const itype_id &type, + int &quantity, const std::function &filter ) { - std::list ret; + ItemList ret; // Handle infinite map sources. - item water = water_from( p ); + item &water = water_from( p ); if( water.typeId() == type ) { - ret.push_back( water ); + ret.push_back( &water ); quantity = 0; return ret; } if( const cata::optional vp = veh_at( p ).part_with_feature( "CARGO", true ) ) { - std::list tmp = use_amount_stack( vp->vehicle().get_items( vp->part_index() ), type, - quantity, filter ); - ret.splice( ret.end(), tmp ); + ItemList tmp = use_amount_stack( vp->vehicle().get_items( vp->part_index() ), type, + quantity, filter ); + ret.insert( ret.end(), tmp.begin(), tmp.end() ); } - std::list tmp = use_amount_stack( i_at( p ), type, quantity, filter ); - ret.splice( ret.end(), tmp ); + ItemList tmp = use_amount_stack( i_at( p ), type, quantity, filter ); + ret.insert( ret.end(), tmp.begin(), tmp.end() ); return ret; } -std::list map::use_amount( const tripoint &origin, const int range, const itype_id &type, - int &quantity, const std::function &filter ) +ItemList map::use_amount( const tripoint &origin, const int range, const itype_id &type, + int &quantity, const std::function &filter ) { - std::list ret; + ItemList ret; for( int radius = 0; radius <= range && quantity > 0; radius++ ) { for( const tripoint &p : points_in_radius( origin, radius ) ) { if( rl_dist( origin, p ) >= radius ) { - std::list tmp = use_amount_square( p, type, quantity, filter ); - ret.splice( ret.end(), tmp ); + ItemList tmp = use_amount_square( p, type, quantity, filter ); + ret.insert( ret.end(), tmp.begin(), tmp.end() ); } } } @@ -4833,12 +4850,12 @@ std::list map::use_amount( const tripoint &origin, const int range, const } template -std::list use_charges_from_stack( Stack stack, const itype_id &type, int &quantity, - const tripoint &pos, const std::function &filter ) +ItemList use_charges_from_stack( Stack stack, const itype_id &type, int &quantity, + const tripoint &pos, const std::function &filter ) { - std::list ret; + ItemList ret; for( auto a = stack.begin(); a != stack.end() && quantity > 0; ) { - if( !a->made_of( LIQUID ) && a->use_charges( type, quantity, ret, pos, filter ) ) { + if( !( *a )->made_of( LIQUID ) && ( *a )->use_charges( type, quantity, ret, pos, filter ) ) { a = stack.erase( a ); } else { ++a; @@ -4848,7 +4865,7 @@ std::list use_charges_from_stack( Stack stack, const itype_id &type, int & } static void use_charges_from_furn( const furn_t &f, const itype_id &type, int &quantity, - map *m, const tripoint &p, std::list &ret, + map *m, const tripoint &p, std::vector &ret, const std::function &filter ) { if( m->has_flag( "LIQUIDCONT", p ) ) { @@ -4856,19 +4873,20 @@ static void use_charges_from_furn( const furn_t &f, const itype_id &type, int &q auto current_item = item_list.begin(); for( ; current_item != item_list.end(); ++current_item ) { // looking for a liquid that matches - if( filter( *current_item ) && current_item->made_of( LIQUID ) && type == current_item->typeId() ) { + if( filter( **current_item ) && ( *current_item )->made_of( LIQUID ) && + type == ( *current_item )->typeId() ) { ret.push_back( *current_item ); - if( current_item->charges - quantity > 0 ) { + if( ( *current_item )->charges - quantity > 0 ) { // Update the returned liquid amount to match the requested amount - ret.back().charges = quantity; + ret.back()->charges = quantity; // Update the liquid item in the world to contain the leftover liquid - current_item->charges -= quantity; + ( *current_item )->charges -= quantity; // All the liquid needed was found, no other sources will be needed quantity = 0; } else { // The liquid copy in ret already contains how much was available // The leftover quantity returned will check other sources - quantity -= current_item->charges; + quantity -= ( *current_item )->charges; // Remove liquid item from the world item_list.erase( current_item ); } @@ -4882,10 +4900,11 @@ static void use_charges_from_furn( const furn_t &f, const itype_id &type, int &q if( itt.item_tags.count( flag_USES_GRID_POWER ) > 0 ) { const tripoint_abs_ms abspos( m->getabs( p ) ); auto &grid = get_distribution_grid_tracker().grid_at( abspos ); - item furn_item( itt.get_id(), calendar::start_of_cataclysm, grid.get_resource() ); + item *furn_item = item::spawn_temporary( itt.get_id(), calendar::start_of_cataclysm, + grid.get_resource() ); int initial_quantity = quantity; - if( filter( furn_item ) ) { - furn_item.use_charges( type, quantity, ret, p ); + if( filter( *furn_item ) ) { + furn_item->use_charges( type, quantity, ret, p ); // That quantity math thing is atrocious. Punishment for the int& "argument". grid.mod_resource( quantity - initial_quantity ); } @@ -4893,33 +4912,35 @@ static void use_charges_from_furn( const furn_t &f, const itype_id &type, int &q const itype_id ammo = ammotype( *itt.tool->ammo_id.begin() )->default_ammotype(); auto stack = m->i_at( p ); auto iter = std::find_if( stack.begin(), stack.end(), - [ammo]( const item & i ) { - return i.typeId() == ammo; + [ammo]( const item * const & i ) { + return i->typeId() == ammo; } ); if( iter != stack.end() ) { - item furn_item( itt.get_id(), calendar::start_of_cataclysm, iter->charges ); - if( !filter( furn_item ) ) { + //TODO!: wtf is this + item *furn_item = item::spawn_temporary( itt.get_id(), calendar::start_of_cataclysm, + ( *iter )->charges ); + if( !filter( *furn_item ) ) { return; } // The item constructor limits the charges to the (type specific) maximum. // Setting it separately circumvents that it is synchronized with the code that creates // the pseudo item (and fills its charges) in inventory.cpp - furn_item.charges = iter->charges; - if( furn_item.use_charges( type, quantity, ret, p ) ) { + furn_item->charges = ( *iter )->charges; + if( furn_item->use_charges( type, quantity, ret, p ) ) { stack.erase( iter ); } else { - iter->charges = furn_item.charges; + ( *iter )->charges = furn_item->charges; } } } } } -std::list map::use_charges( const tripoint &origin, const int range, - const itype_id &type, int &quantity, - const std::function &filter, basecamp *bcp ) +ItemList map::use_charges( const tripoint &origin, const int range, + const itype_id &type, int &quantity, + const std::function &filter, basecamp *bcp ) { - std::list ret; + ItemList ret; // populate a grid of spots that can be reached std::vector reachable_pts; @@ -4929,10 +4950,10 @@ std::list map::use_charges( const tripoint &origin, const int range, // first for( const tripoint &p : reachable_pts ) { // Handle infinite map sources. - item water = water_from( p ); + item &water = water_from( p ); if( water.typeId() == type ) { water.charges = quantity; - ret.push_back( water ); + ret.push_back( &water ); quantity = 0; return ret; } @@ -4954,8 +4975,8 @@ std::list map::use_charges( const tripoint &origin, const int range, } if( accessible_items( p ) ) { - std::list tmp = use_charges_from_stack( i_at( p ), type, quantity, p, filter ); - ret.splice( ret.end(), tmp ); + std::vector tmp = use_charges_from_stack( i_at( p ), type, quantity, p, filter ); + ret.insert( ret.end(), tmp.begin(), tmp.end() ); if( quantity <= 0 ) { return ret; } @@ -4987,10 +5008,11 @@ std::list map::use_charges( const tripoint &origin, const int range, } // TODO: add a sane birthday arg - item tmp( type, calendar::start_of_cataclysm ); - tmp.charges = kpart->vehicle().drain( ftype, quantity ); + //TODO!: check + item *tmp = item::spawn( type, calendar::start_of_cataclysm ); + tmp->charges = kpart->vehicle().drain( ftype, quantity ); // TODO: Handle water poison when crafting starts respecting it - quantity -= tmp.charges; + quantity -= tmp->charges; ret.push_back( tmp ); if( quantity == 0 ) { @@ -5007,9 +5029,9 @@ std::list map::use_charges( const tripoint &origin, const int range, ftype = itype_battery; } // TODO: add a sane birthday arg - item tmp( type, calendar::start_of_cataclysm ); - tmp.charges = weldpart->vehicle().drain( ftype, quantity ); - quantity -= tmp.charges; + item *tmp = item::spawn( type, calendar::start_of_cataclysm ); + tmp->charges = weldpart->vehicle().drain( ftype, quantity ); + quantity -= tmp->charges; ret.push_back( tmp ); if( quantity == 0 ) { @@ -5031,9 +5053,10 @@ std::list map::use_charges( const tripoint &origin, const int range, } // TODO: add a sane birthday arg - item tmp( type, calendar::start_of_cataclysm ); - tmp.charges = craftpart->vehicle().drain( ftype, quantity ); - quantity -= tmp.charges; + //TODO!:check + item *tmp = item::spawn( type, calendar::start_of_cataclysm ); + tmp->charges = craftpart->vehicle().drain( ftype, quantity ); + quantity -= tmp->charges; ret.push_back( tmp ); if( quantity == 0 ) { @@ -5048,10 +5071,11 @@ std::list map::use_charges( const tripoint &origin, const int range, ftype = itype_battery; } + //TODO!: wow what great factoring // TODO: add a sane birthday arg - item tmp( type, calendar::start_of_cataclysm ); - tmp.charges = forgepart->vehicle().drain( ftype, quantity ); - quantity -= tmp.charges; + item *tmp = item::spawn( type, calendar::start_of_cataclysm ); + tmp->charges = forgepart->vehicle().drain( ftype, quantity ); + quantity -= tmp->charges; ret.push_back( tmp ); if( quantity == 0 ) { @@ -5067,9 +5091,9 @@ std::list map::use_charges( const tripoint &origin, const int range, } // TODO: add a sane birthday arg - item tmp( type, calendar::start_of_cataclysm ); - tmp.charges = kilnpart->vehicle().drain( ftype, quantity ); - quantity -= tmp.charges; + item *tmp = item::spawn( type, calendar::start_of_cataclysm ); + tmp->charges = kilnpart->vehicle().drain( ftype, quantity ); + quantity -= tmp->charges; ret.push_back( tmp ); if( quantity == 0 ) { @@ -5089,9 +5113,9 @@ std::list map::use_charges( const tripoint &origin, const int range, } // TODO: add a sane birthday arg - item tmp( type, calendar::start_of_cataclysm ); - tmp.charges = chempart->vehicle().drain( ftype, quantity ); - quantity -= tmp.charges; + item *tmp = item::spawn( type, calendar::start_of_cataclysm ); + tmp->charges = chempart->vehicle().drain( ftype, quantity ); + quantity -= tmp->charges; ret.push_back( tmp ); if( quantity == 0 ) { @@ -5107,9 +5131,9 @@ std::list map::use_charges( const tripoint &origin, const int range, } // TODO: add a sane birthday arg - item tmp( type, calendar::start_of_cataclysm ); - tmp.charges = autoclavepart->vehicle().drain( ftype, quantity ); - quantity -= tmp.charges; + item *tmp = item::spawn( type, calendar::start_of_cataclysm ); + tmp->charges = autoclavepart->vehicle().drain( ftype, quantity ); + quantity -= tmp->charges; ret.push_back( tmp ); if( quantity == 0 ) { @@ -5118,10 +5142,10 @@ std::list map::use_charges( const tripoint &origin, const int range, } if( cargo ) { - std::list tmp = + ItemList tmp = use_charges_from_stack( cargo->vehicle().get_items( cargo->part_index() ), type, quantity, p, filter ); - ret.splice( ret.end(), tmp ); + ret.insert( ret.end(), tmp.begin(), tmp.end() ); if( quantity <= 0 ) { return ret; } @@ -7104,9 +7128,9 @@ template void map::remove_rotten_items( Container &items, const tripoint &pnt ) { for( auto it = items.begin(); it != items.end(); ) { - if( it->has_rotten_away( pnt ) ) { - if( it->is_comestible() ) { - rotten_item_spawn( *it, pnt ); + if( ( *it )->has_rotten_away( pnt ) ) { + if( ( *it )->is_comestible() ) { + rotten_item_spawn( **it, pnt ); } it = i_rem( pnt, it ); } else { @@ -7154,12 +7178,12 @@ void map::fill_funnels( const tripoint &p, const time_point &since ) units::volume maxvolume = 0_ml; auto biggest_container = items.end(); for( auto candidate = items.begin(); candidate != items.end(); ++candidate ) { - if( candidate->is_funnel_container( maxvolume ) ) { + if( ( *candidate )->is_funnel_container( maxvolume ) ) { biggest_container = candidate; } } if( biggest_container != items.end() ) { - retroactively_fill_from_funnel( *biggest_container, tr, since, calendar::turn, getabs( p ) ); + retroactively_fill_from_funnel( **biggest_container, tr, since, calendar::turn, getabs( p ) ); } } @@ -7171,11 +7195,12 @@ void map::grow_plant( const tripoint &p ) } // Can't use item_stack::only_item() since there might be fertilizer map_stack items = i_at( p ); - map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.is_seed(); + map_stack::iterator seed_it = std::find_if( items.begin(), + items.end(), []( const item * const & it ) { + return it->is_seed(); } ); - if( seed == items.end() ) { + if( seed_it == items.end() ) { // No seed there anymore, we don't know what kind of plant it was. // TODO: Fix point types const oter_id ot = overmap_buffer.ter( project_to( tripoint_abs_ms( getabs( p ) ) ) ); @@ -7185,6 +7210,8 @@ void map::grow_plant( const tripoint &p ) furn_set( p, f_null ); return; } + + item *seed = *seed_it; const time_duration plantEpoch = seed->get_plant_epoch(); if( seed->age() >= plantEpoch * furn.plant->growth_multiplier && !furn.has_flag( "GROWTH_HARVEST" ) ) { @@ -7194,8 +7221,9 @@ void map::grow_plant( const tripoint &p ) } // Remove fertilizer if any - map_stack::iterator fertilizer = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.has_flag( "FERTILIZER" ); + map_stack::iterator fertilizer = std::find_if( items.begin(), + items.end(), []( const item * const & it ) { + return it->has_flag( "FERTILIZER" ); } ); if( fertilizer != items.end() ) { items.erase( fertilizer ); @@ -7209,8 +7237,9 @@ void map::grow_plant( const tripoint &p ) } // Remove fertilizer if any - map_stack::iterator fertilizer = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.has_flag( "FERTILIZER" ); + map_stack::iterator fertilizer = std::find_if( items.begin(), + items.end(), []( const item * const & it ) { + return it->has_flag( "FERTILIZER" ); } ); if( fertilizer != items.end() ) { items.erase( fertilizer ); @@ -7330,13 +7359,14 @@ void map::produce_sap( const tripoint &p, const time_duration &time_since_last_a return; } - item sap( "maple_sap", calendar::turn ); - // Is there a proper container? auto items = i_at( p ); for( auto &it : items ) { - if( it.is_bucket() || it.is_watertight_container() ) { - const int capacity = it.get_remaining_capacity_for_liquid( sap, true ); + if( it->is_bucket() || it->is_watertight_container() ) { + + item &sap = *item::spawn( "maple_sap", calendar::turn ); + + const int capacity = it->get_remaining_capacity_for_liquid( sap, true ); if( capacity > 0 ) { new_charges = std::min( new_charges, capacity ); @@ -7344,7 +7374,9 @@ void map::produce_sap( const tripoint &p, const time_duration &time_since_last_a sap.poison = one_in( 10 ) ? 1 : 0; sap.charges = new_charges; - it.fill_with( sap ); + it->fill_with( sap ); + } else { + sap.destroy(); } // Only fill up the first container. break; @@ -8459,15 +8491,15 @@ void map::draw_circle_furn( const furn_id &type, const point &p, int rad ) void map::add_corpse( const tripoint &p ) { - item body; + item *body; const bool isReviveSpecial = one_in( 10 ); if( !isReviveSpecial ) { - body = item::make_corpse(); + body = &item::make_corpse(); } else { - body = item::make_corpse( mon_zombie ); - body.set_flag( "REVIVE_SPECIAL" ); + body = &item::make_corpse( mon_zombie ); + body->set_flag( "REVIVE_SPECIAL" ); } put_items_from_loc( item_group_id( "default_zombie_clothes" ), p ); @@ -8475,7 +8507,7 @@ void map::add_corpse( const tripoint &p ) put_items_from_loc( item_group_id( "default_zombie_items" ), p ); } - add_item_or_charges( p, body ); + add_item_or_charges( p, *body ); } field &map::get_field( const tripoint &p ) @@ -8642,16 +8674,16 @@ tripoint_range map::points_on_zlevel() const return points_on_zlevel( abs_sub.z ); } -std::list map::get_active_items_in_radius( const tripoint ¢er, +std::vector map::get_active_items_in_radius( const tripoint ¢er, int radius ) const { return get_active_items_in_radius( center, radius, special_item_type::none ); } -std::list map::get_active_items_in_radius( const tripoint ¢er, int radius, +std::vector map::get_active_items_in_radius( const tripoint ¢er, int radius, special_item_type type ) const { - std::list result; + std::vector result; const point minp( center.xy() + point( -radius, -radius ) ); const point maxp( center.xy() + point( radius, radius ) ); @@ -8670,17 +8702,15 @@ std::list map::get_active_items_in_radius( const tripoint ¢er const point sm_offset( submap_loc.x * SEEX, submap_loc.y * SEEY ); submap *sm = get_submap_at_grid( submap_loc ); - std::vector items = type == special_item_type::none ? sm->active_items.get() : + std::vector items = type == special_item_type::none ? sm->active_items.get() : sm->active_items.get_special( type ); for( const auto &elem : items ) { - const tripoint pos( sm_offset + elem.location, submap_loc.z ); - - if( rl_dist( pos, center ) > radius ) { + if( rl_dist( elem->position(), center ) > radius ) { continue; } - if( elem.item_ref ) { - result.emplace_back( map_cursor( pos ), elem.item_ref.get() ); + if( elem ) { + result.emplace_back( elem ); } } } diff --git a/src/map.h b/src/map.h index 56296b8ec72d..3da106e27456 100644 --- a/src/map.h +++ b/src/map.h @@ -52,7 +52,6 @@ class character_id; class computer; class field; class field_entry; -class item_location; class map_cursor; class mapgendata; class monster; @@ -107,9 +106,9 @@ class map_stack : public item_stack tripoint location; map *myorigin; public: - map_stack( cata::colony *newstack, tripoint newloc, map *neworigin ) : + map_stack( std::vector *newstack, tripoint newloc, map *neworigin ) : item_stack( newstack ), location( newloc ), myorigin( neworigin ) {} - void insert( const item &newitem ) override; + void insert( item &newitem ) override; iterator erase( const_iterator it ) override; int count_limit() const override { return MAX_ITEM_IN_SQUARE; @@ -1240,7 +1239,7 @@ class map map_stack i_at( const point &p ) { return i_at( tripoint( p, abs_sub.z ) ); } - item water_from( const tripoint &p ); + item &water_from( const tripoint &p ); void i_clear( const tripoint &p ); void i_clear( const point &p ) { i_clear( tripoint( p, abs_sub.z ) ); @@ -1290,8 +1289,8 @@ class map * @return reference to dropped (and possibly stacked) item or null item on failure * @warning function is relatively expensive and meant for user initiated actions, not mapgen */ - item &add_item_or_charges( const tripoint &pos, item obj, bool overflow = true ); - item &add_item_or_charges( const point &p, item obj, bool overflow = true ) { + item &add_item_or_charges( const tripoint &pos, item &obj, bool overflow = true ); + item &add_item_or_charges( const point &p, item &obj, bool overflow = true ) { return add_item_or_charges( tripoint( p, abs_sub.z ), obj, overflow ); } @@ -1302,12 +1301,12 @@ class map * * @returns The item that got added, or nulitem. */ - item &add_item( const tripoint &p, item new_item ); - void add_item( const point &p, item new_item ) { + item &add_item( const tripoint &p, item &new_item ); + void add_item( const point &p, item &new_item ) { add_item( tripoint( p, abs_sub.z ), new_item ); } - item &spawn_an_item( const tripoint &p, item new_item, int charges, int damlevel ); - void spawn_an_item( const point &p, item new_item, int charges, int damlevel ) { + item &spawn_an_item( const tripoint &p, item &new_item, int charges, int damlevel ); + void spawn_an_item( const point &p, item &new_item, int charges, int damlevel ) { spawn_an_item( tripoint( p, abs_sub.z ), new_item, charges, damlevel ); } @@ -1315,12 +1314,12 @@ class map * Update an item's active status, for example when adding * hot or perishable liquid to a container. */ - void make_active( item_location &loc ); + void make_active( item &loc ); /** * Update luminosity before and after item's transformation */ - void update_lum( item_location &loc, bool add ); + void update_lum( item &loc, bool add ); /** * @name Consume items on the map @@ -1335,13 +1334,13 @@ class map * somewhere else. */ /*@{*/ - std::list use_amount_square( const tripoint &p, const itype_id &type, - int &quantity, const std::function &filter = return_true ); - std::list use_amount( const tripoint &origin, int range, const itype_id &type, + ItemList use_amount_square( const tripoint &p, const itype_id &type, int &quantity, const std::function &filter = return_true ); - std::list use_charges( const tripoint &origin, int range, const itype_id &type, - int &quantity, const std::function &filter = return_true, - basecamp *bcp = nullptr ); + ItemList use_amount( const tripoint &origin, int range, const itype_id &type, + int &quantity, const std::function &filter = return_true ); + ItemList use_charges( const tripoint &origin, int range, const itype_id &type, + int &quantity, const std::function &filter = return_true, + basecamp *bcp = nullptr ); /*@}*/ /** @@ -1382,8 +1381,8 @@ class map const time_point &turn = calendar::start_of_cataclysm ); // Similar to spawn_an_item, but spawns a list of items, or nothing if the list is empty. - std::vector spawn_items( const tripoint &p, const std::vector &new_items ); - void spawn_items( const point &p, const std::vector &new_items ) { + std::vector spawn_items( const tripoint &p, std::vector new_items ); + void spawn_items( const point &p, std::vector new_items ) { spawn_items( tripoint( p, abs_sub.z ), new_items ); } @@ -2077,8 +2076,8 @@ class map /// returns an empty range. tripoint_range points_on_zlevel( int z ) const; - std::list get_active_items_in_radius( const tripoint ¢er, int radius ) const; - std::list get_active_items_in_radius( const tripoint ¢er, int radius, + std::vector get_active_items_in_radius( const tripoint ¢er, int radius ) const; + std::vector get_active_items_in_radius( const tripoint ¢er, int radius, special_item_type type ) const; /**returns positions of furnitures with matching flag in the specified radius*/ diff --git a/src/map_extras.cpp b/src/map_extras.cpp index 2cdbadd67f7b..27ab2e1549f0 100644 --- a/src/map_extras.cpp +++ b/src/map_extras.cpp @@ -1145,7 +1145,7 @@ static bool mx_minefield( map &m_orig, const tripoint &abs_sub ) //50% chance to spawn a dead soldier with a trail of blood if( one_in( 2 ) ) { m.add_splatter_trail( fd_blood, { 17, 6, abs_sub.z }, { 19, 3, abs_sub.z } ); - item body = item::make_corpse(); + item &body = item::make_corpse(); m.put_items_from_loc( item_group_id( "mon_zombie_soldier_death_drops" ), { 17, 5, abs_sub.z } ); m.add_item_or_charges( { 17, 5, abs_sub.z }, body ); } @@ -1241,7 +1241,7 @@ static bool mx_minefield( map &m_orig, const tripoint &abs_sub ) m.add_field( { loc.xy(), abs_sub.z }, fd_gibs_flesh, rng( 1, 3 ) ); } } - item body = item::make_corpse(); + item &body = item::make_corpse(); m.put_items_from_loc( item_group_id( "mon_zombie_soldier_death_drops" ), { 11, 21, abs_sub.z } ); m.add_item_or_charges( { 11, 21, abs_sub.z }, body ); } @@ -1477,7 +1477,7 @@ static bool mx_minefield( map &m_orig, const tripoint &abs_sub ) //50% chance to spawn a soldier killed by gunfire, and a trail of blood if( one_in( 2 ) ) { m.add_splatter_trail( fd_blood, { 14, 5, abs_sub.z }, { 17, 5, abs_sub.z } ); - item body = item::make_corpse(); + item &body = item::make_corpse(); m.put_items_from_loc( item_group_id( "mon_zombie_soldier_death_drops" ), { 15, 5, abs_sub.z } ); m.add_item_or_charges( { 15, 5, abs_sub.z }, body ); } @@ -1521,7 +1521,7 @@ static bool mx_minefield( map &m_orig, const tripoint &abs_sub ) m.spawn_item( { 23, 11, abs_sub.z }, itype_hatchet ); //Spawn chopped soldier corpse - item body = item::make_corpse(); + item &body = item::make_corpse(); m.put_items_from_loc( item_group_id( "mon_zombie_soldier_death_drops" ), { 23, 12, abs_sub.z } ); m.add_item_or_charges( { 23, 12, abs_sub.z }, body ); m.add_field( { 23, 12, abs_sub.z }, fd_gibs_flesh, rng( 1, 3 ) ); @@ -2162,8 +2162,8 @@ static void burned_ground_parser( map &m, const tripoint &loc ) while( m.flammable_items_at( loc ) ) { map_stack stack = m.i_at( loc ); for( auto it = stack.begin(); it != stack.end(); ) { - if( it->flammable() ) { - m.create_burnproducts( loc, *it, it->weight() ); + if( ( *it )->flammable() ) { + m.create_burnproducts( loc, **it, ( *it )->weight() ); it = stack.erase( it ); } else { it++; @@ -2595,11 +2595,11 @@ static bool mx_mayhem( map &m, const tripoint &abs_sub ) } const int max_wolves = rng( 1, 3 ); - item body = item::make_corpse( mon_wolf ); if( one_in( 2 ) ) { //...from the north for( int i = 0; i < max_wolves; i++ ) { const auto &loc = m.points_in_radius( { 12, 12, abs_sub.z }, 3 ); const tripoint where = random_entry( loc ); + item &body = item::make_corpse( mon_wolf ); m.add_item_or_charges( where, body ); m.add_field( where, fd_blood, rng( 1, 3 ) ); } @@ -2607,6 +2607,7 @@ static bool mx_mayhem( map &m, const tripoint &abs_sub ) for( int i = 0; i < max_wolves; i++ ) { const auto &loc = m.points_in_radius( { 12, 18, abs_sub.z }, 3 ); const tripoint where = random_entry( loc ); + item &body = item::make_corpse( mon_wolf ); m.add_item_or_charges( where, body ); m.add_field( where, fd_blood, rng( 1, 3 ) ); } @@ -2844,7 +2845,7 @@ static bool mx_grave( map &m, const tripoint &abs_sub ) //Pets' corpses const std::vector pets = MonsterGroupManager::GetMonstersFromGroup( GROUP_PETS ); const mtype_id &pet = random_entry_ref( pets ); - item body = item::make_corpse( pet, calendar::start_of_cataclysm ); + item &body = item::make_corpse( pet, calendar::start_of_cataclysm ); m.add_item_or_charges( corpse_location, body ); } //5% chance to spawn easter egg grave(s) diff --git a/src/map_field.cpp b/src/map_field.cpp index 58ada964ebab..0678269e5ddd 100644 --- a/src/map_field.cpp +++ b/src/map_field.cpp @@ -537,16 +537,18 @@ void map::process_fields_in_submap( submap *const current_submap, // without forcing the function to use i_at( p ) for fires without items if( !is_sealed && map_tile.get_item_count() > 0 ) { map_stack items_here = i_at( p ); - std::vector new_content; + std::vector new_content; for( auto explosive = items_here.begin(); explosive != items_here.end(); ) { - if( explosive->will_explode_in_fire() ) { + if( ( *explosive )->will_explode_in_fire() ) { + //TODO!: check becuase this shit is predictable now // We need to make a copy because the iterator validity is not predictable - item copy = *explosive; + item *copy = *explosive; explosive = items_here.erase( explosive ); - if( copy.detonate( p, new_content ) ) { + if( copy->detonate( p, new_content ) ) { // Need to restart, iterators may not be valid explosive = items_here.begin(); } + copy->destroy(); } else { ++explosive; } @@ -556,7 +558,8 @@ void map::process_fields_in_submap( submap *const current_submap, // The highest # of items this fire can remove in one turn int max_consume = cur.get_field_intensity() * 2; - for( auto fuel = items_here.begin(); fuel != items_here.end() && consumed < max_consume; ) { + for( auto fuel_it = items_here.begin(); fuel_it != items_here.end() && consumed < max_consume; ) { + item *fuel = *fuel_it; // `item::burn` modifies the charges in order to simulate some of them getting // destroyed by the fire, this changes the item weight, but may not actually // destroy it. We need to spawn products anyway. @@ -573,16 +576,16 @@ void map::process_fields_in_submap( submap *const current_submap, if( destroyed ) { // If we decided the item was destroyed by fire, remove it. // But remember its contents, except for irremovable mods, if any - const std::list content_list = fuel->contents.all_items_top(); + const std::vector content_list = fuel->contents.all_items_top(); for( item *it : content_list ) { if( !it->is_irremovable() ) { - new_content.push_back( item( *it ) ); + new_content.push_back( it ); } } - fuel = items_here.erase( fuel ); + fuel_it = items_here.erase( fuel_it ); consumed++; } else { - ++fuel; + ++fuel_it; } } @@ -639,7 +642,8 @@ void map::process_fields_in_submap( submap *const current_submap, if( cur.get_field_intensity() > 1 && one_in( 200 - cur.get_field_intensity() * 50 ) ) { furn_set( p, f_ash ); - add_item_or_charges( p, item( "ash" ) ); + //TODO!: check + add_item_or_charges( p, *item::spawn( "ash" ) ); } } @@ -988,11 +992,12 @@ void map::process_fields_in_submap( submap *const current_submap, if( cur_fd_type_id == fd_push_items ) { map_stack items = i_at( p ); for( auto pushee = items.begin(); pushee != items.end(); ) { - if( pushee->typeId() != itype_rock || - pushee->age() < 1_turns ) { + if( ( *pushee )->typeId() != itype_rock || + ( *pushee )->age() < 1_turns ) { pushee++; } else { - item tmp = *pushee; + //TODO!: check + item &tmp = **pushee; tmp.set_age( 0_turns ); pushee = items.erase( pushee ); std::vector valid; @@ -1504,9 +1509,9 @@ void map::player_in_field( player &u ) for( int i = 0; i < rng( 1, 7 ); i++ ) { bodypart_id bp = u.get_random_body_part(); int sum_cover = 0; - for( const item &i : u.worn ) { - if( i.covers( bp->token ) ) { - sum_cover += i.get_coverage(); + for( const item * const &i : u.worn ) { + if( i->covers( bp->token ) ) { + sum_cover += i->get_coverage(); } } // Get stung if [clothing on a body part isn't thick enough (like t-shirt) OR clothing covers less than 100% of body part] diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 66046af5c181..e431115b4904 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -1125,7 +1125,7 @@ class jmapgen_liquid_item : public jmapgen_piece } void apply( mapgendata &dat, const jmapgen_int &x, const jmapgen_int &y ) const override { if( one_in( chance.get() ) ) { - item newliquid( liquid, calendar::start_of_cataclysm ); + item &newliquid = *item::spawn( liquid, calendar::start_of_cataclysm ); if( amount.valmax > 0 ) { newliquid.charges = amount.get(); } @@ -1192,7 +1192,7 @@ class jmapgen_loot : public jmapgen_piece void apply( mapgendata &dat, const jmapgen_int &x, const jmapgen_int &y ) const override { if( rng( 0, 99 ) < chance ) { const Item_spawn_data *const isd = &result_group; - const std::vector spawn = isd->create( calendar::start_of_cataclysm ); + const std::vector spawn = isd->create( calendar::start_of_cataclysm ); dat.m.spawn_items( tripoint( rng( x.val, x.valmax ), rng( y.val, y.valmax ), dat.m.get_abs_sub().z ), spawn ); } @@ -4160,7 +4160,7 @@ void map::draw_lab( mapgendata &dat ) point( marker_x, marker_y ), "mininuke", 1, 1, calendar::start_of_cataclysm, rng( 2, 4 ) ); } else { - item newliquid( "plut_slurry_dense", calendar::start_of_cataclysm ); + item &newliquid = *item::spawn( "plut_slurry_dense", calendar::start_of_cataclysm ); newliquid.charges = 1; add_item_or_charges( tripoint( marker_x, marker_y, get_abs_sub().z ), newliquid ); @@ -5445,7 +5445,7 @@ void map::place_spawns( const mongroup_id &group, const int chance, void map::place_gas_pump( const point &p, int charges, const std::string &fuel_type ) { - item fuel( fuel_type, calendar::start_of_cataclysm ); + item &fuel = *item::spawn( fuel_type, calendar::start_of_cataclysm ); fuel.charges = charges; add_item( p, fuel ); ter_set( p, ter_id( fuel.fuel_pump_terrain() ) ); @@ -5458,7 +5458,7 @@ void map::place_gas_pump( const point &p, int charges ) void map::place_toilet( const point &p, int charges ) { - item water( "water", calendar::start_of_cataclysm ); + item &water = *item::spawn( "water", calendar::start_of_cataclysm ); water.charges = charges; add_item( p, water ); furn_set( p, f_toilet ); @@ -5503,8 +5503,8 @@ void map::apply_faction_ownership( const point &p1, const point &p2, const facti for( const tripoint &p : points_in_rectangle( tripoint( p1, abs_sub.z ), tripoint( p2, abs_sub.z ) ) ) { auto items = i_at( p.xy() ); - for( item &elem : items ) { - elem.set_owner( id ); + for( item * const &elem : items ) { + elem->set_owner( id ); } vehicle *source_veh = veh_pointer_or_null( veh_at( p ) ); if( source_veh ) { @@ -5565,7 +5565,7 @@ std::vector map::place_items( const item_group_id &loc, const int chance for( auto e : res ) { if( e->is_tool() || e->is_gun() || e->is_magazine() ) { if( rng( 0, 99 ) < magazine && !e->magazine_integral() && !e->magazine_current() ) { - e->put_in( item( e->magazine_default(), e->birthday() ) ); + e->put_in( *item::spawn( e->magazine_default(), e->birthday() ) ); } if( rng( 0, 99 ) < ammo && e->ammo_remaining() == 0 ) { e->ammo_set( e->ammo_default(), e->ammo_capacity() ); diff --git a/src/martialarts.cpp b/src/martialarts.cpp index b6b420f481fe..6c25c08faaa4 100644 --- a/src/martialarts.cpp +++ b/src/martialarts.cpp @@ -454,8 +454,8 @@ bool ma_requirements::is_valid_character( const Character &u ) const bool is_armed = u.is_armed(); bool unarmed_weapon = is_armed && u.used_weapon().has_flag( flag_UNARMED_WEAPON ); bool forced_unarmed = u.martial_arts_data->selected_force_unarmed(); - bool weapon_ok = is_valid_weapon( u.weapon ); - bool style_weapon = u.martial_arts_data->selected_has_weapon( u.weapon.typeId() ); + bool weapon_ok = is_valid_weapon( u.get_weapon() ); + bool style_weapon = u.martial_arts_data->selected_has_weapon( u.get_weapon().typeId() ); bool all_weapons = u.martial_arts_data->selected_allow_melee(); bool unarmed_ok = !is_armed || ( unarmed_weapon && unarmed_weapons_allowed ); @@ -914,7 +914,7 @@ ma_technique character_martial_arts::get_miss_recovery_tec( const item &weap ) c // This one isn't used with a weapon bool character_martial_arts::has_grab_break_tec() const { - for( const matec_id &technique : get_all_techniques( item() ) ) { + for( const matec_id &technique : get_all_techniques( null_item_reference() ) ) { if( technique->grab_break ) { return true; } @@ -1245,14 +1245,14 @@ bool player::can_autolearn( const matype_id &ma_id ) const void character_martial_arts::martialart_use_message( const Character &owner ) const { martialart ma = style_selected.obj(); - if( ma.force_unarmed || ma.weapon_valid( owner.weapon ) ) { + if( ma.force_unarmed || ma.weapon_valid( owner.get_weapon() ) ) { owner.add_msg_if_player( m_info, _( ma.get_initiate_avatar_message() ) ); } else if( ma.strictly_melee && !owner.is_armed() ) { owner.add_msg_if_player( m_bad, _( "%s cannot be used unarmed." ), ma.name ); } else if( ma.strictly_unarmed && owner.is_armed() ) { owner.add_msg_if_player( m_bad, _( "%s cannot be used with weapons." ), ma.name ); } else { - owner.add_msg_if_player( m_bad, _( "The %1$s is not a valid %2$s weapon." ), owner.weapon.tname( 1, + owner.add_msg_if_player( m_bad, _( "The %1$s is not a valid %2$s weapon." ), owner.get_weapon().tname( 1, false ), ma.name ); } } @@ -1499,7 +1499,7 @@ bool ma_style_callback::key( const input_context &ctxt, const input_event &event std::vector weapons; std::transform( ma.weapons.begin(), ma.weapons.end(), std::back_inserter( weapons ), []( const itype_id & wid )-> std::string { - if( item::nname( wid ) == get_player_character().weapon.display_name() ) + if( item::nname( wid ) == get_player_character().get_weapon().display_name() ) { return colorize( item::nname( wid ) + _( " (wielded)" ), c_light_cyan ); } else diff --git a/src/material.cpp b/src/material.cpp index 6385b6ee6b36..17be1b8fd080 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -138,9 +138,9 @@ void material_type::check() const } } for( auto &ci : _compacts_into ) { - if( + if( //TODO!: move this to type !ci.is_valid() || - !item( ci, calendar::start_of_cataclysm ).only_made_of( std::set { id } ) + !item::spawn_temporary( ci, calendar::start_of_cataclysm )->only_made_of( std::set { id } ) ) { debugmsg( "invalid \"compacts_into\" %s for %s.", ci.c_str(), id.c_str() ); } diff --git a/src/mattack_actors.cpp b/src/mattack_actors.cpp index 1e95da3fa810..89b69356d60b 100644 --- a/src/mattack_actors.cpp +++ b/src/mattack_actors.cpp @@ -566,7 +566,7 @@ void gun_actor::shoot( monster &z, const tripoint &target, const gun_mode_id &mo { z.moves -= move_cost; - item gun( gun_type ); + item &gun = *item::spawn( gun_type ); gun.gun_set_mode( mode ); itype_id ammo = ammo_type ? ammo_type : gun.ammo_default(); @@ -594,11 +594,11 @@ void gun_actor::shoot( monster &z, const tripoint &target, const gun_mode_id &mo tmp.set_skill_level( pr.first, pr.second ); } - tmp.weapon = gun; - tmp.i_add( item( "UPS_off", calendar::turn, 1000 ) ); + tmp.set_weapon(gun); + tmp.i_add( *item::spawn( "UPS_off", calendar::turn, 1000 ) ); if( g->u.sees( z ) ) { - add_msg( m_warning, _( description ), z.name(), tmp.weapon.tname() ); + add_msg( m_warning, _( description ), z.name(), tmp.get_weapon().tname() ); } z.ammo[ammo] -= tmp.fire_gun( target, gun.gun_current_mode().qty ); diff --git a/src/melee.cpp b/src/melee.cpp index 17338d4c2610..8f53455dc173 100644 --- a/src/melee.cpp +++ b/src/melee.cpp @@ -134,7 +134,7 @@ std::string melee_message( const ma_technique &tec, Character &p, const item &Character::used_weapon() const { - return martial_arts_data->selected_force_unarmed() ? null_item_reference() : weapon; + return martial_arts_data->selected_force_unarmed() ? null_item_reference() : *weapon; } item &Character::used_weapon() @@ -144,7 +144,7 @@ item &Character::used_weapon() bool Character::is_armed() const { - return !weapon.is_null(); + return !weapon->is_null(); } bool Character::unarmed_attack() const @@ -191,15 +191,15 @@ bool Character::handle_melee_wear( item &shield, float wear_multiplier ) const std::set blacklist = { itype_rag, itype_leather, itype_fur }; for( auto &comp : shield.components ) { - if( blacklist.count( comp.typeId() ) <= 0 ) { - if( weak_chip > comp.chip_resistance() ) { - weak_chip = comp.chip_resistance(); - weak_comp = comp.typeId(); + if( blacklist.count( comp->typeId() ) <= 0 ) { + if( weak_chip > comp->chip_resistance() ) { + weak_chip = comp->chip_resistance(); + weak_comp = comp->typeId(); } } - if( comp.volume() > big_vol ) { - big_vol = comp.volume(); - big_comp = comp.typeId(); + if( comp->volume() > big_vol ) { + big_vol = comp->volume(); + big_comp = comp->typeId(); } } material_factor = ( weak_chip < INT_MAX ? weak_chip : shield.chip_resistance() ) / fragile_factor; @@ -228,38 +228,39 @@ bool Character::handle_melee_wear( item &shield, float wear_multiplier ) // Dump its contents on the ground // Destroy irremovable mods, if any - - for( auto mod : shield.gunmods() ) { + std::vector to_be_destroyed; + for( item *mod : shield.gunmods() ) { if( mod->is_irremovable() ) { - remove_item( *mod ); + to_be_destroyed.push_back( mod ); } } - // Preserve item temporarily for component breakdown - item temp = shield; + for( item *mod : to_be_destroyed ) { + mod->destroy(); + } shield.contents.spill_contents( pos() ); - remove_item( shield ); + shield.destroy(); // Breakdown fragile weapons into components - if( temp.has_flag( "FRAGILE_MELEE" ) && !temp.components.empty() ) { + if( shield.has_flag( "FRAGILE_MELEE" ) && !shield.components.empty() ) { add_msg_player_or_npc( m_bad, _( "Your %s breaks apart!" ), _( "'s %s breaks apart!" ), str ); - for( auto &comp : temp.components ) { - int break_chance = comp.typeId() == weak_comp ? 2 : 8; + for( auto &comp : shield.components ) { + int break_chance = comp->typeId() == weak_comp ? 2 : 8; if( one_in( break_chance ) ) { - add_msg_if_player( m_bad, _( "The %s is destroyed!" ), comp.tname() ); + add_msg_if_player( m_bad, _( "The %s is destroyed!" ), comp->tname() ); continue; } - if( comp.typeId() == big_comp && !is_armed() ) { - wield( comp ); + if( comp->typeId() == big_comp && !is_armed() ) { + wield( *comp ); } else { - g->m.add_item_or_charges( pos(), comp ); + g->m.add_item_or_charges( pos(), *comp ); } } } else { @@ -416,7 +417,7 @@ void Character::melee_attack( Creature &t, bool allow_special, const matec_id &f return; } } - item &cur_weapon = allow_unarmed ? used_weapon() : weapon; + item &cur_weapon = allow_unarmed ? used_weapon() : *weapon; if( cur_weapon.attack_cost() > attack_cost( cur_weapon ) * 20 ) { add_msg( m_bad, _( "This weapon is too unwieldy to attack with!" ) ); @@ -624,7 +625,7 @@ void player::reach_attack( const tripoint &p ) { matec_id force_technique = tec_none; /** @EFFECT_MELEE >5 allows WHIP_DISARM technique */ - if( weapon.has_flag( "WHIP" ) && ( get_skill_level( skill_melee ) > 5 ) && one_in( 3 ) ) { + if( get_weapon().has_flag( "WHIP" ) && ( get_skill_level( skill_melee ) > 5 ) && one_in( 3 ) ) { force_technique = matec_id( "WHIP_DISARM" ); } @@ -637,7 +638,7 @@ void player::reach_attack( const tripoint &p ) // Max out recoil recoil = MAX_RECOIL; - int move_cost = attack_cost( weapon ); + int move_cost = attack_cost( get_weapon() ); int skill = std::min( 10, get_skill_level( skill_stabbing ) ); int t = 0; std::vector path = line_to( pos(), p, t, 0 ); @@ -662,19 +663,19 @@ void player::reach_attack( const tripoint &p ) rand.y = last_point.y; } - here.bash( rand, str_cur + weapon.damage_melee( DT_BASH ) ); - handle_melee_wear( weapon ); + here.bash( rand, str_cur + get_weapon().damage_melee( DT_BASH ) ); + handle_melee_wear( get_weapon() ); mod_moves( -move_cost ); return; /** @EFFECT_STABBING increases ability to reach attack through fences */ } else if( here.impassable( path_point ) && // Fences etc. Spears can stab through those - !( weapon.has_flag( "SPEAR" ) && + !( get_weapon().has_flag( "SPEAR" ) && g->m.has_flag( "THIN_OBSTACLE", path_point ) && x_in_y( skill, 10 ) ) ) { /** @EFFECT_STR increases bash effects when reach attacking past something */ - here.bash( path_point, str_cur + weapon.damage_melee( DT_BASH ) ); - handle_melee_wear( weapon ); + here.bash( path_point, str_cur + get_weapon().damage_melee( DT_BASH ) ); + handle_melee_wear( get_weapon() ); mod_moves( -move_cost ); return; } @@ -689,15 +690,15 @@ void player::reach_attack( const tripoint &p ) rand.y = last_point.y; } - here.bash( rand, str_cur + weapon.damage_melee( DT_BASH ) ); - handle_melee_wear( weapon ); + here.bash( rand, str_cur + get_weapon().damage_melee( DT_BASH ) ); + handle_melee_wear( get_weapon() ); mod_moves( -move_cost ); return; } if( critter == nullptr ) { add_msg_if_player( _( "You swing at the air." ) ); - if( martial_arts_data->has_miss_recovery_tec( weapon ) ) { + if( martial_arts_data->has_miss_recovery_tec( get_weapon() ) ) { move_cost /= 3; // "Probing" is faster than a regular miss } @@ -1469,7 +1470,7 @@ void Character::perform_technique( const ma_technique &technique, Creature &t, d _( " disarms %s and takes their weapon!" ), p->name ); } - item it = p->remove_weapon(); + item &it = p->remove_weapon(); wield( it ); } @@ -1544,26 +1545,26 @@ static int blocking_ability( const item &shield ) item &Character::best_shield() { // Note: wielded weapon, not one used for attacks - int best_value = blocking_ability( weapon ); + int best_value = blocking_ability( *weapon ); // "BLOCK_WHILE_WORN" without a blocking tech need to be worn for the bonus best_value = best_value == 2 ? 0 : best_value; - item *best = best_value > 0 ? &weapon : &null_item_reference(); - for( item &shield : worn ) { - if( shield.has_flag( "BLOCK_WHILE_WORN" ) && blocking_ability( shield ) >= best_value ) { + item *best = best_value > 0 ? weapon : &null_item_reference(); + for( item * const &shield : worn ) { + if( shield->has_flag( "BLOCK_WHILE_WORN" ) && blocking_ability( *shield ) >= best_value ) { // in case a mod adds a shield that protects only one arm, the corresponding arm needs to be working - if( shield.covers( bp_arm_l ) || shield.covers( bp_arm_r ) ) { - if( shield.covers( bp_arm_l ) && !is_limb_disabled( bodypart_id( "arm_l" ) ) ) { - best = &shield; - } else if( shield.covers( bp_arm_r ) && !is_limb_disabled( bodypart_id( "arm_r" ) ) ) { - best = &shield; + if( shield->covers( bp_arm_l ) || shield->covers( bp_arm_r ) ) { + if( shield->covers( bp_arm_l ) && !is_limb_disabled( bodypart_id( "arm_l" ) ) ) { + best = shield; + } else if( shield->covers( bp_arm_r ) && !is_limb_disabled( bodypart_id( "arm_r" ) ) ) { + best = shield; } // leg guards - } else if( ( shield.covers( bp_leg_l ) || shield.covers( bp_leg_r ) ) && + } else if( ( shield->covers( bp_leg_l ) || shield->covers( bp_leg_r ) ) && get_working_leg_count() >= 1 ) { - best = &shield; + best = shield; // in case a mod adds an unusual worn blocking item, like a magic bracelet/crown, it's handled here } else { - best = &shield; + best = shield; } } } @@ -1596,7 +1597,7 @@ bool Character::block_hit( Creature *source, bodypart_id &bp_hit, damage_instanc item &shield = best_shield(); block_bonus = blocking_ability( shield ); bool conductive_shield = shield.conductive(); - bool unarmed = weapon.has_flag( "UNARMED_WEAPON" ) || weapon.is_null(); + bool unarmed = weapon->has_flag( "UNARMED_WEAPON" ) || weapon->is_null(); bool force_unarmed = martial_arts_data->is_force_unarmed(); int melee_skill = get_skill_level( skill_melee ); @@ -1775,7 +1776,7 @@ bool Character::block_hit( Creature *source, bodypart_id &bp_hit, damage_instanc if( tec != tec_none && !is_dead_state() ) { if( get_stamina() < get_stamina_max() / 3 ) { add_msg( m_bad, _( "You try to counterattack but you are too exhausted!" ) ); - } else if( weapon.made_of( material_id( "glass" ) ) ) { + } else if( weapon->made_of( material_id( "glass" ) ) ) { add_msg( m_bad, _( "The item you are wielding is too fragile to counterattack with!" ) ); } else { melee_attack( *source, false, tec ); @@ -1819,7 +1820,7 @@ std::string Character::melee_special_effects( Creature &t, damage_instance &d, i std::string target = t.disp_name(); if( has_active_bionic( bionic_id( "bio_shock" ) ) && get_power_level() >= 2_kJ && - ( !is_armed() || weapon.conductive() ) ) { + ( !is_armed() || weapon->conductive() ) ) { mod_power_level( -2_kJ ); d.add_damage( DT_ELECTRIC, rng( 2, 10 ) ); @@ -1840,7 +1841,7 @@ std::string Character::melee_special_effects( Creature &t, damage_instance &d, i } } - if( weapon.has_flag( "FLAMING" ) ) { + if( weapon->has_flag( "FLAMING" ) ) { d.add_damage( DT_HEAT, rng( 1, 8 ) ); if( is_player() ) { @@ -1921,7 +1922,7 @@ static damage_instance hardcoded_mutation_attack( const Character &u, const trai num_attacks = 7; } // Note: we're counting arms, so we want wielded item here, not weapon used for attack - if( u.weapon.is_two_handed( u ) || !u.has_two_arms() || u.worn_with_flag( "RESTRICT_HANDS" ) ) { + if( u.get_weapon().is_two_handed( u ) || !u.has_two_arms() || u.worn_with_flag( "RESTRICT_HANDS" ) ) { num_attacks--; } @@ -2292,7 +2293,7 @@ double player::melee_value( const item &weap ) const my_value *= 1.0f + 0.5f * ( std::sqrt( reach ) - 1.0f ); } // value polearms less to account for the trickiness of keeping the right range - if( weapon.has_flag( "POLEARM" ) ) { + if( weap.has_flag( "POLEARM" ) ) { my_value *= 0.8; } @@ -2309,7 +2310,7 @@ double player::melee_value( const item &weap ) const double player::unarmed_value() const { // TODO: Martial arts - return melee_value( item() ); + return melee_value( null_item_reference() ); } void player::disarm( npc &target ) @@ -2334,13 +2335,13 @@ void player::disarm( npc &target ) their_roll += dice( 3, target.get_per() ); their_roll += dice( 3, target.get_skill_level( skill_melee ) ); - item &it = target.weapon; + item &it = target.get_weapon(); // roll your melee and target's dodge skills to check if grab/smash attack succeeds int hitspread = target.deal_melee_attack( this, hit_roll() ); if( hitspread < 0 ) { add_msg( _( "You lunge for the %s, but miss!" ), it.tname() ); - mod_moves( -100 - stumble( *this, weapon ) - attack_cost( weapon ) ); + mod_moves( -100 - stumble( *this, it ) - attack_cost( it ) ); target.on_attacked( *this ); return; } @@ -2356,7 +2357,7 @@ void player::disarm( npc &target ) //~ %1$s: weapon name, %2$s: NPC name add_msg( _( "You forcefully take %1$s from %2$s!" ), it.tname(), target.name ); // wield() will deduce our moves, consider to deduce more/less moves for balance - item rem_it = target.i_rem( &it ); + item &rem_it = target.i_rem( &it ); wield( rem_it ); } else if( my_roll >= their_roll / 2 ) { add_msg( _( "You grab at %s and pull with all your force, but it drops nearby!" ), @@ -2374,7 +2375,7 @@ void player::disarm( npc &target ) } // Make their weapon fall on floor if we've rolled enough. - mod_moves( -100 - attack_cost( weapon ) ); + mod_moves( -100 - attack_cost( it ) ); if( my_roll >= their_roll ) { add_msg( _( "You smash %s with all your might forcing their %s to drop down nearby!" ), target.name, it.tname() ); @@ -2395,7 +2396,7 @@ void avatar::steal( npc &target ) return; } - item_location loc = game_menus::inv::steal( *this, target ); + item* loc = game_menus::inv::steal( *this, target ); if( !loc ) { return; } @@ -2413,7 +2414,7 @@ void avatar::steal( npc &target ) int their_roll = dice( 5, target.get_per() ); - const item *it = loc.get_item(); + const item *it = loc; if( my_roll >= their_roll ) { add_msg( _( "You sneakily steal %1$s from %2$s!" ), it->tname(), target.name ); diff --git a/src/memorial_logger.cpp b/src/memorial_logger.cpp index 4bfcd4c5661c..7e6e58de912f 100644 --- a/src/memorial_logger.cpp +++ b/src/memorial_logger.cpp @@ -315,12 +315,12 @@ void memorial_logger::write( std::ostream &file, const std::string &epitaph ) co //Equipment file << _( "Weapon:" ) << eol; - file << indent << u.weapon.invlet << " - " << u.weapon.tname( 1, false ) << eol; + file << indent << u.get_weapon().invlet << " - " << u.get_weapon().tname( 1, false ) << eol; file << eol; file << _( "Equipment:" ) << eol; - for( const item &elem : u.worn ) { - item next_item = elem; + for( const item * const &elem : u.worn ) { + const item &next_item = *elem; file << indent << next_item.invlet << " - " << next_item.tname( 1, false ); if( next_item.charges > 0 ) { file << " (" << next_item.charges << ")"; @@ -335,8 +335,8 @@ void memorial_logger::write( std::ostream &file, const std::string &epitaph ) co file << _( "Inventory:" ) << eol; u.inv.restack( u ); invslice slice = u.inv.slice(); - for( const std::list *elem : slice ) { - const item &next_item = elem->front(); + for( const ItemList *elem : slice ) { + const item &next_item = *elem->front(); file << indent << next_item.invlet << " - " << next_item.tname( static_cast( elem->size() ), false ); if( elem->size() > 1 ) { diff --git a/src/mission.cpp b/src/mission.cpp index fe7276238618..b0156eea19dc 100644 --- a/src/mission.cpp +++ b/src/mission.cpp @@ -490,7 +490,8 @@ void mission::get_all_item_group_matches( std::vector &items, //recursivly check item contents for target if( itm->is_container() && !itm->is_container_empty() ) { - std::list content_list = itm->contents.all_items_top(); + //TODO!: check, this is some weird shit + std::vector content_list = itm->contents.all_items_top(); std::vector content = std::vector(); //list of item into list item* diff --git a/src/mission_companion.cpp b/src/mission_companion.cpp index 3d9132475946..f3b519879169 100644 --- a/src/mission_companion.cpp +++ b/src/mission_companion.cpp @@ -1065,14 +1065,14 @@ void talk_function::field_plant( npc &p, const std::string &place ) //Plant the actual seeds for( const tripoint &plot : bay.points_on_zlevel() ) { if( bay.ter( plot ) == t_dirtmound && limiting_number > 0 ) { - std::list used_seed; + ItemList used_seed; if( item::count_by_charges( seed_id ) ) { used_seed = g->u.use_charges( seed_id, 1 ); } else { used_seed = g->u.use_amount( seed_id, 1 ); } - used_seed.front().set_age( 0_turns ); - bay.add_item_or_charges( plot, used_seed.front() ); + used_seed.front()->set_age( 0_turns ); + bay.add_item_or_charges( plot, *used_seed.front() ); bay.set( plot, t_dirt, f_plant_seed ); limiting_number--; } @@ -1090,7 +1090,8 @@ void talk_function::field_harvest( npc &p, const std::string &place ) const tripoint_abs_omt site = overmap_buffer.find_closest( player_character.global_omt_location(), place, 20, false ); tinymap bay; - item tmp; + //TODO!: cheeeeck, tmp needs some proper management + item *tmp; std::vector seed_types; std::vector plant_types; std::vector plant_names; @@ -1099,23 +1100,23 @@ void talk_function::field_harvest( npc &p, const std::string &place ) map_stack items = bay.i_at( plot ); if( bay.furn( plot ) == furn_str_id( "f_plant_harvest" ) && !items.empty() ) { // Can't use item_stack::only_item() since there might be fertilizer - map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.is_seed(); + map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item * const & it ) { + return it->is_seed(); } ); if( seed != items.end() ) { - const islot_seed &seed_data = *seed->type->seed; - tmp = item( seed_data.fruit_id, calendar::turn ); + const islot_seed &seed_data = *( *seed )->type->seed; + tmp = item::spawn( seed_data.fruit_id, calendar::turn ); bool check = false; for( const std::string &elem : plant_names ) { - if( elem == tmp.type_name( 3 ) ) { + if( elem == tmp->type_name( 3 ) ) { check = true; } } if( !check ) { - plant_types.push_back( tmp.typeId() ); - plant_names.push_back( tmp.type_name( 3 ) ); - seed_types.push_back( seed->typeId() ); + plant_types.push_back( tmp->typeId() ); + plant_names.push_back( tmp->type_name( 3 ) ); + seed_types.push_back( ( *seed )->typeId() ); } } } @@ -1145,14 +1146,14 @@ void talk_function::field_harvest( npc &p, const std::string &place ) if( bay.furn( plot ) == furn_str_id( "f_plant_harvest" ) ) { // Can't use item_stack::only_item() since there might be fertilizer map_stack items = bay.i_at( plot ); - map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.is_seed(); + map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item * const & it ) { + return it->is_seed(); } ); if( seed != items.end() ) { - const islot_seed &seed_data = *seed->type->seed; - tmp = item( seed_data.fruit_id, calendar::turn ); - if( tmp.typeId() == plant_types[plant_index] ) { + const islot_seed &seed_data = *( *seed )->type->seed; + tmp = item::spawn( seed_data.fruit_id, calendar::turn ); + if( tmp->typeId() == plant_types[plant_index] ) { number_plots++; bay.i_clear( plot ); bay.furn_set( plot, f_null ); @@ -1170,8 +1171,8 @@ void talk_function::field_harvest( npc &p, const std::string &place ) } } bay.save(); - tmp = item( plant_types[plant_index], calendar::turn ); - int money = ( number_plants * tmp.price( true ) - number_plots * 2 ) / 100; + tmp = item::spawn( plant_types[plant_index], calendar::turn ); + int money = ( number_plants * tmp->price( true ) - number_plots * 2 ) / 100; bool liquidate = false; signed int a = number_plots * 2; @@ -1187,27 +1188,27 @@ void talk_function::field_harvest( npc &p, const std::string &place ) //Add fruit if( liquidate ) { add_msg( _( "The %s are sold for $%d…" ), plant_names[plant_index], money ); - g->u.cash += ( number_plants * tmp.price( true ) - number_plots * 2 ) / 100; + g->u.cash += ( number_plants * tmp->price( true ) - number_plots * 2 ) / 100; } else { - if( tmp.count_by_charges() ) { - tmp.charges = 1; + if( tmp->count_by_charges() ) { + tmp->charges = 1; } for( int i = 0; i < number_plants; ++i ) { //Should be dropped at your feet once greedy companions can be controlled - g->u.i_add( tmp ); + g->u.i_add( *item::spawn( *tmp ) ); } add_msg( _( "You receive %d %s…" ), number_plants, plant_names[plant_index] ); } - tmp = item( seed_types[plant_index], calendar::turn ); - const islot_seed &seed_data = *tmp.type->seed; + tmp = item::spawn( seed_types[plant_index], calendar::turn ); + const islot_seed &seed_data = *tmp->type->seed; if( seed_data.spawn_seeds ) { - if( tmp.count_by_charges() ) { - tmp.charges = 1; + if( tmp->count_by_charges() ) { + tmp->charges = 1; } for( int i = 0; i < number_seeds; ++i ) { - g->u.i_add( tmp ); + g->u.i_add( *item::spawn( *tmp ) ); } - add_msg( _( "You receive %d %s…" ), number_seeds, tmp.type_name( 3 ) ); + add_msg( _( "You receive %d %s…" ), number_seeds, tmp->type_name( 3 ) ); } } @@ -1334,9 +1335,9 @@ bool talk_function::scavenging_raid_return( npc &p ) itemlist = item_group_id( "npc_weapon_random" ); } auto result = item_group::item_from( itemlist ); - if( !result.is_null() ) { - popup( _( "%s returned with a %s for you!" ), comp->name, result.tname() ); - g->u.i_add( result ); + if( !result->is_null() ) { + popup( _( "%s returned with a %s for you!" ), comp->name, result->tname() ); + g->u.i_add( *result ); } } companion_return( *comp ); @@ -1503,9 +1504,9 @@ bool talk_function::forage_return( npc &p ) } } auto result = item_group::item_from( item_group_id( itemlist ) ); - if( !result.is_null() ) { - popup( _( "%s returned with a %s for you!" ), comp->name, result.tname() ); - g->u.i_add( result ); + if( !result->is_null() ) { + popup( _( "%s returned with a %s for you!" ), comp->name, result->tname() ); + g->u.i_add( *result ); } if( one_in( 6 ) && !p.has_trait( trait_NPC_MISSION_LEV_1 ) ) { p.set_mutation( trait_NPC_MISSION_LEV_1 ); @@ -1760,8 +1761,8 @@ void talk_function::companion_return( npc &comp ) map &here = get_map(); for( size_t i = 0; i < comp.companion_mission_inv.size(); i++ ) { for( const auto &it : comp.companion_mission_inv.const_stack( i ) ) { - if( !it.count_by_charges() || it.charges > 0 ) { - here.add_item_or_charges( g->u.pos(), it ); + if( !it->count_by_charges() || it->charges > 0 ) { + here.add_item_or_charges( g->u.pos(), *it ); } } } @@ -2106,13 +2107,14 @@ void talk_function::loot_building( const tripoint_abs_omt &site ) } //Hoover up tasty items! map_stack items = bay.i_at( p ); - for( map_stack::iterator it = items.begin(); it != items.end(); ) { + for( map_stack::iterator iter = items.begin(); iter != items.end(); ) { + item *it = *iter; if( ( ( it->is_food() || it->is_food_container() ) && !one_in( 8 ) ) || ( it->made_of( LIQUID ) && !one_in( 8 ) ) || ( it->price( true ) > 1000 && !one_in( 4 ) ) || one_in( 5 ) ) { - it = items.erase( it ); + iter = items.erase( iter ); } else { - ++it; + ++iter; } } } diff --git a/src/mission_end.cpp b/src/mission_end.cpp index c9cabb65f959..4a1d8abbfa24 100644 --- a/src/mission_end.cpp +++ b/src/mission_end.cpp @@ -26,6 +26,6 @@ void mission_end::deposit_box( mission *miss ) } else if( one_in( 3 ) ) { itemName = "m4a1"; } - g->u.i_add( item( itemName, calendar::start_of_cataclysm ) ); + g->u.i_add( *item::spawn( itemName, calendar::start_of_cataclysm ) ); add_msg( m_good, _( "%s gave you an item from the deposit box." ), p->name ); } diff --git a/src/mission_start.cpp b/src/mission_start.cpp index 6f50b0d525ed..d135eaa66100 100644 --- a/src/mission_start.cpp +++ b/src/mission_start.cpp @@ -59,7 +59,7 @@ void mission_start::place_dog( mission *miss ) debugmsg( "Couldn't find NPC! %d", miss->npc_id.get_value() ); return; } - g->u.i_add( item( "dog_whistle", calendar::start_of_cataclysm ) ); + g->u.i_add( *item::spawn( "dog_whistle", calendar::start_of_cataclysm ) ); add_msg( _( "%s gave you a dog whistle." ), dev->name ); miss->target = house; @@ -192,7 +192,7 @@ void mission_start::place_npc_software( mission *miss ) debugmsg( "Couldn't find NPC! %d", miss->npc_id.get_value() ); return; } - g->u.i_add( item( "usb_drive", calendar::start_of_cataclysm ) ); + g->u.i_add( *item::spawn( "usb_drive", calendar::start_of_cataclysm ) ); add_msg( _( "%s gave you a USB drive." ), dev->name ); std::string type = "house"; diff --git a/src/monattack.cpp b/src/monattack.cpp index 2be7db818e80..4a88a47f2dde 100644 --- a/src/monattack.cpp +++ b/src/monattack.cpp @@ -352,16 +352,16 @@ bool mattack::eat_food( monster *z ) auto items = g->m.i_at( p ); for( auto &item : items ) { //Fun limit prevents scavengers from eating feces - if( !item.is_food() || item.get_comestible_fun() < -20 ) { + if( !item->is_food() || item->get_comestible_fun() < -20 ) { continue; } //Don't eat own eggs - if( z->type->baby_egg != item.type->get_id() ) { + if( z->type->baby_egg != item->type->get_id() ) { int consumed = 1; - if( item.count_by_charges() ) { - g->m.use_charges( p, 0, item.type->get_id(), consumed ); + if( item->count_by_charges() ) { + g->m.use_charges( p, 0, item->type->get_id(), consumed ); } else { - g->m.use_amount( p, 0, item.type->get_id(), consumed ); + g->m.use_amount( p, 0, item->type->get_id(), consumed ); } return true; } @@ -390,7 +390,7 @@ bool mattack::antqueen( monster *z ) if( g->is_empty( dest ) && g->m.has_items( dest ) ) { for( auto &i : g->m.i_at( dest ) ) { - if( i.typeId() == itype_ant_egg ) { + if( i->typeId() == itype_ant_egg ) { egg_points.push_back( dest ); // Done looking at this tile break; @@ -424,7 +424,7 @@ bool mattack::antqueen( monster *z ) for( const tripoint &egg_pos : egg_points ) { map_stack items = g->m.i_at( egg_pos ); for( map_stack::iterator it = items.begin(); it != items.end(); ) { - if( it->typeId() != itype_ant_egg ) { + if( ( *it )->typeId() != itype_ant_egg ) { ++it; continue; } @@ -809,11 +809,11 @@ bool mattack::pull_metal_weapon( monster *z ) player *foe = dynamic_cast< player * >( target ); if( foe != nullptr ) { // Wielded steel or iron items except for built-in things like bionic claws or monomolecular blade - if( !foe->weapon.has_flag( "NO_UNWIELD" ) && - ( foe->weapon.made_of( material_id( "iron" ) ) || - foe->weapon.made_of( material_id( "hardsteel" ) ) || - foe->weapon.made_of( material_id( "steel" ) ) || - foe->weapon.made_of( material_id( "budget_steel" ) ) ) ) { + if( !foe->get_weapon().has_flag( "NO_UNWIELD" ) && + ( foe->get_weapon().made_of( material_id( "iron" ) ) || + foe->get_weapon().made_of( material_id( "hardsteel" ) ) || + foe->get_weapon().made_of( material_id( "steel" ) ) || + foe->get_weapon().made_of( material_id( "budget_steel" ) ) ) ) { int wp_skill = foe->get_skill_level( skill_melee ); // It takes a while z->moves -= att_cost_pull; @@ -826,7 +826,7 @@ bool mattack::pull_metal_weapon( monster *z ) auto m_type = foe == &g->u ? m_bad : m_neutral; if( rng( 1, 100 ) <= success ) { target->add_msg_player_or_npc( m_type, _( "%s is pulled away from your hands!" ), - _( "%s is pulled away from 's hands!" ), foe->weapon.tname() ); + _( "%s is pulled away from 's hands!" ), foe->get_weapon().tname() ); z->add_item( foe->remove_weapon() ); if( foe->has_activity( ACT_RELOAD ) ) { foe->cancel_activity(); @@ -995,8 +995,8 @@ bool mattack::resurrect( monster *z ) } for( auto &i : g->m.i_at( p ) ) { - const mtype *mt = i.get_mtype(); - if( !( i.is_corpse() && i.can_revive() && i.active && mt->has_flag( MF_REVIVES ) && + const mtype *mt = i->get_mtype(); + if( !( i->is_corpse() && i->can_revive() && i->active && mt->has_flag( MF_REVIVES ) && mt->in_species( ZOMBIE ) && !mt->has_flag( MF_NO_NECRO ) ) ) { continue; } @@ -1013,10 +1013,10 @@ bool mattack::resurrect( monster *z ) } return false; } - int raise_score = ( i.damage_level( 4 ) + 1 ) * mt->hp + i.burnt; + int raise_score = ( i->damage_level( 4 ) + 1 ) * mt->hp + i->burnt; lowest_raise_score = std::min( lowest_raise_score, raise_score ); if( raise_score <= raising_level ) { - corpses.push_back( std::make_pair( p, &i ) ); + corpses.push_back( std::make_pair( p, i ) ); } } } @@ -2713,7 +2713,7 @@ bool mattack::grab( monster *z ) return true; } - item &cur_weapon = pl->weapon; + item &cur_weapon = pl->get_weapon(); ///\EFFECT_DEX increases chance to avoid being grabbed if( pl->can_grab_break( cur_weapon ) && rng( 0, pl->get_dex() ) > rng( 0, z->type->melee_sides + z->type->melee_dice ) ) { @@ -2721,7 +2721,7 @@ bool mattack::grab( monster *z ) target->add_msg_if_player( m_info, _( "The %s tries to grab you as well, but you bat it away!" ), z->name() ); } else if( pl->is_throw_immune() && ( !pl->is_armed() || - pl->martial_arts_data->selected_has_weapon( pl->weapon.typeId() ) ) ) { + pl->martial_arts_data->selected_has_weapon( pl->get_weapon().typeId() ) ) ) { target->add_msg_if_player( m_info, _( "The %s tries to grab you…" ), z->name() ); thrown_by_judo( z ); } else if( pl->has_grab_break_tec() ) { @@ -3111,8 +3111,8 @@ bool mattack::check_money_left( monster *z ) z->friendly = 0; if( !z->inv.empty() ) { - for( const item &it : z->inv ) { - g->m.add_item_or_charges( z->pos(), it ); + for( item *&it : z->inv ) { + g->m.add_item_or_charges( z->pos(), *it ); } z->inv.clear(); z->remove_effect( effect_has_bag ); @@ -3241,7 +3241,7 @@ bool mattack::photograph( monster *z ) } } - if( z->friendly || g->u.weapon.typeId() == itype_e_handcuffs ) { + if( z->friendly || g->u.get_weapon().typeId() == itype_e_handcuffs ) { // Friendly (hacked?) bot ignore the player. Arrested suspect ignored too. // TODO: might need to be revisited when it can target npcs. return false; @@ -3259,7 +3259,7 @@ bool mattack::photograph( monster *z ) string_format( _( "a robotic voice boom, \"Citizen %s!\"" ), cname ), false, "speech", z->type->id.str() ); - if( g->u.weapon.is_gun() ) { + if( g->u.get_weapon().is_gun() ) { sounds::sound( z->pos(), 15, sounds::sound_t::alert, _( "\"Drop your gun! Now!\"" ) ); } else if( g->u.is_armed() ) { sounds::sound( z->pos(), 15, sounds::sound_t::alert, _( "\"Drop your weapon! Now!\"" ) ); @@ -3318,6 +3318,7 @@ void mattack::rifle( monster *z, Creature *target ) z->ammo[ammo_type] = 3000; } + //TODO!: check alllla this shit npc tmp = make_fake_npc( z, 16, 10, 8, 12 ); tmp.set_skill_level( skill_rifle, 8 ); tmp.set_skill_level( skill_gun, 6 ); @@ -3346,11 +3347,10 @@ void mattack::rifle( monster *z, Creature *target ) if( g->u.sees( *z ) ) { add_msg( m_warning, _( "The %s opens up with its rifle!" ), z->name() ); } + tmp.set_weapon(item::spawn( "m4a1" )->ammo_set( ammo_type, z->ammo[ ammo_type ] )); + int burst = std::max( tmp.get_weapon().gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 ); - tmp.weapon = item( "m4a1" ).ammo_set( ammo_type, z->ammo[ ammo_type ] ); - int burst = std::max( tmp.weapon.gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 ); - - z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.weapon.ammo_required(); + z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.get_weapon().ammo_required(); if( target == &g->u ) { z->add_effect( effect_targeted, 3_turns ); @@ -3407,10 +3407,10 @@ void mattack::frag( monster *z, Creature *target ) // This is for the bots, not add_msg( m_warning, _( "The %s's grenade launcher fires!" ), z->name() ); } - tmp.weapon = item( "mgl" ).ammo_set( ammo_type, z->ammo[ ammo_type ] ); - int burst = std::max( tmp.weapon.gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 ); + tmp.set_weapon(item::spawn( "mgl" )->ammo_set( ammo_type, z->ammo[ ammo_type ] )); + int burst = std::max( tmp.get_weapon().gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 ); - z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.weapon.ammo_required(); + z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.get_weapon().ammo_required(); if( target == &g->u ) { z->add_effect( effect_targeted, 3_turns ); @@ -3466,10 +3466,10 @@ void mattack::tankgun( monster *z, Creature *target ) if( g->u.sees( *z ) ) { add_msg( m_warning, _( "The %s's 120mm cannon fires!" ), z->name() ); } - tmp.weapon = item( "TANK" ).ammo_set( ammo_type, z->ammo[ ammo_type ] ); - int burst = std::max( tmp.weapon.gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 ); + tmp.set_weapon(item::spawn( "TANK" )->ammo_set( ammo_type, z->ammo[ ammo_type ] )); + int burst = std::max( tmp.get_weapon().gun_get_mode( gun_mode_id( "AUTO" ) ).qty, 1 ); - z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.weapon.ammo_required(); + z->ammo[ ammo_type ] -= tmp.fire_gun( target->pos(), burst ) * tmp.get_weapon().ammo_required(); } bool mattack::searchlight( monster *z ) @@ -3491,7 +3491,7 @@ bool mattack::searchlight( monster *z ) for( int i = 0; i < max_lamp_count; i++ ) { - item settings( "processor", calendar::start_of_cataclysm ); + item &settings = *item::spawn( "processor", calendar::start_of_cataclysm ); settings.set_var( "SL_PREFER_UP", "TRUE" ); settings.set_var( "SL_PREFER_DOWN", "TRUE" ); @@ -3539,7 +3539,7 @@ bool mattack::searchlight( monster *z ) if( !generator_ok ) { for( auto &settings : z->inv ) { - settings.set_var( "SL_POWER", "OFF" ); + settings->set_var( "SL_POWER", "OFF" ); } return true; @@ -3548,7 +3548,7 @@ bool mattack::searchlight( monster *z ) for( int i = 0; i < max_lamp_count; i++ ) { - item &settings = z->inv[i]; + item &settings = *z->inv[i]; if( settings.get_var( "SL_POWER" ) == "OFF" ) { return true; @@ -3785,7 +3785,7 @@ bool mattack::copbot( monster *z ) // TODO: Make it recognize zeds as human, but ignore animals player *foe = dynamic_cast( target ); bool sees_u = foe != nullptr && z->sees( *foe ); - bool cuffed = foe != nullptr && foe->weapon.typeId() == itype_e_handcuffs; + bool cuffed = foe != nullptr && foe->get_weapon().typeId() == itype_e_handcuffs; // Taze first, then ask questions (simplifies later checks for non-humans) if( !cuffed && is_adjacent( z, target, true ) ) { taze( z, target ); @@ -4294,35 +4294,35 @@ bool mattack::absorb_meat( monster *z ) for( const auto &p : g->m.points_in_radius( z->pos(), 1 ) ) { auto items = g->m.i_at( p ); for( auto ¤t_item : items ) { - const material_id current_item_material = current_item.get_base_material().ident(); + const material_id current_item_material = current_item->get_base_material().ident(); if( current_item_material == material_id( "flesh" ) || current_item_material == material_id( "hflesh" ) ) { //We have something meaty! Calculate how much it will heal the monster - const int ml_of_meat = units::to_milliliter( current_item.volume() ); - const int total_charges = current_item.count(); + const int ml_of_meat = units::to_milliliter( current_item->volume() ); + const int total_charges = current_item->count(); const int ml_per_charge = ml_of_meat / total_charges; //We have a max size of meat here to avoid absorbing whole corpses. if( ml_per_charge > max_meat_absorbed * 1000 ) { add_msg( m_info, _( "The %1$s quivers hungrily in the direction of the %2$s." ), z->name(), - current_item.tname() ); + current_item->tname() ); return false; } - if( current_item.count_by_charges() ) { + if( current_item->count_by_charges() ) { //Choose a random amount of meat charges to absorb int meat_absorbed = std::min( max_meat_absorbed, rng( 1, total_charges ) ); const int hp_to_heal = meat_absorbed * ml_per_charge * meat_absorption_factor; z->heal( hp_to_heal, true ); - g->m.use_charges( p, 0, current_item.type->get_id(), meat_absorbed ); + g->m.use_charges( p, 0, current_item->type->get_id(), meat_absorbed ); } else { //Only absorb one meaty item int meat_absorbed = 1; const int hp_to_heal = meat_absorbed * ml_per_charge * meat_absorption_factor; z->heal( hp_to_heal, true ); - g->m.use_amount( p, 0, current_item.type->get_id(), meat_absorbed ); + g->m.use_amount( p, 0, current_item->type->get_id(), meat_absorbed ); } if( g->u.sees( *z ) ) { add_msg( m_warning, _( "The %1$s absorbs the %2$s, growing larger." ), z->name(), - current_item.tname() ); + current_item->tname() ); add_msg( m_debug, "The %1$s now has %2$s out of %3$s hp", z->name(), z->get_hp(), z->get_hp_max() ); } @@ -4745,7 +4745,7 @@ bool mattack::riotbot( monster *z ) //already arrested? //and yes, if the player has no hands, we are not going to arrest him. if( foe != nullptr && - ( foe->weapon.typeId() == itype_e_handcuffs || !foe->has_two_arms() ) ) { + ( foe->get_weapon().typeId() == itype_e_handcuffs || !foe->has_two_arms() ) ) { z->anger = 0; if( calendar::once_every( 25_turns ) ) { @@ -4803,7 +4803,7 @@ bool mattack::riotbot( monster *z ) if( choice == ur_arrest ) { z->anger = 0; - item handcuffs( "e_handcuffs", calendar::start_of_cataclysm ); + item &handcuffs = *item::spawn( "e_handcuffs", calendar::start_of_cataclysm ); handcuffs.charges = handcuffs.type->maximum_charges(); handcuffs.active = true; handcuffs.set_var( "HANDCUFFS_X", foe->posx() ); @@ -5272,7 +5272,7 @@ bool mattack::bio_op_takedown( monster *z ) foe->add_effect( effect_downed, 3_turns ); } } else if( ( !foe->is_armed() || - foe->martial_arts_data->selected_has_weapon( foe->weapon.typeId() ) ) && + foe->martial_arts_data->selected_has_weapon( foe->get_weapon().typeId() ) ) && !thrown_by_judo( z ) ) { // Saved by the tentacle-bracing! :) hit = bodypart_id( "torso" ); @@ -5418,7 +5418,7 @@ bool mattack::bio_op_disarm( monster *z ) their_roll += dice( 3, foe->get_per() ); their_roll += dice( 3, foe->get_skill_level( skill_melee ) ); - item &it = foe->weapon; + item &it = foe->get_weapon(); target->add_msg_if_player( m_bad, _( "The zombie grabs your %s…" ), it.tname() ); @@ -5488,7 +5488,7 @@ bool mattack::kamikaze( monster *z ) if( z->get_effect( effect_countdown ).get_duration() == 1_turns ) { z->die( nullptr ); // Timer is out, detonate - item i_explodes( act_bomb_type, calendar::turn, 0 ); + item &i_explodes = *item::spawn_temporary( act_bomb_type, calendar::turn, 0 ); i_explodes.active = true; i_explodes.process( nullptr, z->pos(), false ); return false; diff --git a/src/mondeath.cpp b/src/mondeath.cpp index d59b54e823f4..65f449c6fbfb 100644 --- a/src/mondeath.cpp +++ b/src/mondeath.cpp @@ -131,7 +131,8 @@ static void scatter_chunks( const itype_id &chunk_name, int chunk_amt, monster & pile_size = std::min( chunk_amt, pile_size ); distance = std::abs( distance ); map &here = get_map(); - const item chunk( chunk_name, calendar::turn, pile_size ); + //TODO!: cheeeeckyyy + item *chunk = item::spawn( chunk_name, calendar::turn, pile_size ); for( int i = 0; i < chunk_amt; i += pile_size ) { bool drop_chunks = true; tripoint tarp( z.pos() + point( rng( -distance, distance ), rng( -distance, distance ) ) ); @@ -178,7 +179,7 @@ static void scatter_chunks( const itype_id &chunk_name, int chunk_amt, monster & prev_point = tarp; } if( drop_chunks ) { - here.add_item_or_charges( tarp, chunk ); + here.add_item_or_charges( tarp, *chunk ); } } } @@ -243,7 +244,7 @@ void mdeath::splatter( monster &z ) } } // add corpse with gib flag - item corpse = item::make_corpse( z.type->id, calendar::turn, z.unique_name, z.get_upgrade_time() ); + item &corpse = item::make_corpse( z.type->id, calendar::turn, z.unique_name, z.get_upgrade_time() ); // Set corpse to damage that aligns with being pulped corpse.set_damage( 4000 ); corpse.set_flag( "GIBBED" ); @@ -607,7 +608,7 @@ void mdeath::focused_beam( monster &z ) { map_stack items = g->m.i_at( z.pos() ); for( map_stack::iterator it = items.begin(); it != items.end(); ) { - if( it->typeId() == itype_processor ) { + if( ( *it )->typeId() == itype_processor ) { it = items.erase( it ); } else { ++it; @@ -620,7 +621,7 @@ void mdeath::focused_beam( monster &z ) add_msg( m_warning, _( "As the final light is destroyed, it erupts in a blinding flare!" ) ); } - item &settings = z.inv[0]; + item &settings = *z.inv[0]; point p2( z.posx() + settings.get_var( "SL_SPOT_X", 0 ), z.posy() + settings.get_var( "SL_SPOT_Y", 0 ) ); @@ -655,38 +656,40 @@ void mdeath::broken( monster &z ) } // make "broken_manhack", or "broken_eyebot", ... item_id.insert( 0, "broken_" ); - item broken_mon( item_id, calendar::turn ); + item &broken_mon = *item::spawn( item_id, calendar::turn ); const int max_hp = std::max( z.get_hp_max(), 1 ); const float overflow_damage = std::max( -z.get_hp(), 0 ); const float corpse_damage = 2.5 * overflow_damage / max_hp; broken_mon.set_damage( static_cast( std::floor( corpse_damage * itype::damage_scale ) ) ); g->m.add_item_or_charges( z.pos(), broken_mon ); - + //TODO!: push up these temporaries if( z.type->has_flag( MF_DROPS_AMMO ) ) { for( const std::pair &ammo_entry : z.type->starting_ammo ) { if( z.ammo[ammo_entry.first] > 0 ) { bool spawned = false; for( const std::pair &attack : z.type->special_attacks ) { if( attack.second->id == "gun" ) { - item gun = item( dynamic_cast( attack.second.get() )->gun_type ); + item &gun = *item::spawn_temporary( dynamic_cast + ( attack.second.get() )->gun_type ); bool same_ammo = false; for( const ammotype &at : gun.ammo_types() ) { - if( at == item( ammo_entry.first ).ammo_type() ) { + if( at == item::spawn_temporary( ammo_entry.first )->ammo_type() ) { same_ammo = true; break; } } const bool uses_mags = !gun.magazine_compatible().empty(); if( same_ammo && uses_mags ) { - std::vector mags; + std::vector mags; int ammo_count = z.ammo[ammo_entry.first]; while( ammo_count > 0 ) { - item mag = item( gun.type->magazine_default.find( item( ammo_entry.first ).ammo_type() )->second ); - mag.ammo_set( ammo_entry.first, - std::min( ammo_count, mag.type->magazine->capacity ) ); - mags.insert( mags.end(), mag ); - ammo_count -= mag.type->magazine->capacity; + item *mag = item::spawn( gun.type->magazine_default.find( item::spawn_temporary( + ammo_entry.first )->ammo_type() )->second ); + mag->ammo_set( ammo_entry.first, + std::min( ammo_count, mag->type->magazine->capacity ) ); + mags.push_back( mag ); + ammo_count -= mag->type->magazine->capacity; } g->m.spawn_items( z.pos(), mags ); spawned = true; @@ -764,17 +767,17 @@ void mdeath::jabberwock( monster &z ) player *ch = dynamic_cast( z.get_killer() ); bool vorpal = ch && ch->is_player() && - ch->weapon.has_flag( "DIAMOND" ) && - ch->weapon.volume() > 750_ml; + ch->get_weapon().has_flag( "DIAMOND" ) && + ch->get_weapon().volume() > 750_ml; - if( vorpal && !ch->weapon.has_technique( matec_id( "VORPAL" ) ) ) { + if( vorpal && !ch->get_weapon().has_technique( matec_id( "VORPAL" ) ) ) { if( ch->sees( z ) ) { ch->add_msg_if_player( m_info, //~ %s is the possessive form of the monster's name _( "As the flames in %s eyes die out, your weapon seems to shine slightly brighter." ), z.disp_name( true ) ); } - ch->weapon.add_technique( matec_id( "VORPAL" ) ); + ch->get_weapon().add_technique( matec_id( "VORPAL" ) ); } mdeath::normal( z ); @@ -861,7 +864,7 @@ void mdeath::detonate( monster &z ) mdeath::normal( z ); // Then detonate our suicide bombs for( const auto &bombs : dets ) { - item bomb_item( bombs.first, calendar::start_of_cataclysm ); + item &bomb_item = *item::spawn( bombs.first, calendar::start_of_cataclysm ); bomb_item.charges = bombs.second; bomb_item.active = true; g->m.add_item_or_charges( z.pos(), bomb_item ); @@ -878,32 +881,32 @@ void mdeath::broken_ammo( monster &z ) mdeath::broken( z ); } -static std::vector butcher_cbm_item( const itype_id &what, +static std::vector butcher_cbm_item( const itype_id &what, const time_point &birthday, const std::vector &flags, const std::vector &faults ) { - item something( what, birthday ); + item *something = item::spawn( what, birthday ); for( const std::string &flg : flags ) { - something.set_flag( flg ); + something->set_flag( flg ); } for( const fault_id &flt : faults ) { - something.faults.emplace( flt ); + something->faults.emplace( flt ); } return {something}; } -static std::vector butcher_cbm_group( const item_group_id &group, +static std::vector butcher_cbm_group( const item_group_id &group, const time_point &birthday, const std::vector &flags, const std::vector &faults ) { - std::vector spawned = item_group::items_from( group, birthday ); - for( item &it : spawned ) { + std::vector spawned = item_group::items_from( group, birthday ); + for( item * const &it : spawned ) { for( const std::string &flg : flags ) { - it.set_flag( flg ); + it->set_flag( flg ); } for( const fault_id &flt : faults ) { - it.faults.emplace( flt ); + it->faults.emplace( flt ); } } return spawned; @@ -911,7 +914,7 @@ static std::vector butcher_cbm_group( const item_group_id &group, void make_mon_corpse( monster &z, int damageLvl ) { - item corpse = item::make_corpse( z.type->id, calendar::turn, z.unique_name, z.get_upgrade_time() ); + item &corpse = item::make_corpse( z.type->id, calendar::turn, z.unique_name, z.get_upgrade_time() ); corpse.set_damage( damageLvl ); if( z.has_effect( effect_pacified ) && z.type->in_species( ZOMBIE ) ) { // Pacified corpses have a chance of becoming unpacified when regenerating. @@ -924,18 +927,18 @@ void make_mon_corpse( monster &z, int damageLvl ) // Pre-gen bionic on death rather than on butcher for( const harvest_entry &entry : *z.type->harvest ) { if( entry.type == "bionic" || entry.type == "bionic_group" ) { - std::vector contained_bionics = + std::vector contained_bionics = entry.type == "bionic" ? butcher_cbm_item( itype_id( entry.drop ), calendar::turn, entry.flags, entry.faults ) : butcher_cbm_group( item_group_id( entry.drop ), calendar::turn, entry.flags, entry.faults ); - for( const item &it : contained_bionics ) { + for( item *&it : contained_bionics ) { // Disgusting hack: use components instead of contents to hide stuff corpse.components.push_back( it ); } } } } - for( const item &it : z.corpse_components ) { + for( item *&it : z.corpse_components ) { corpse.components.push_back( it ); } get_map().add_item_or_charges( z.pos(), corpse ); diff --git a/src/mondefense.cpp b/src/mondefense.cpp index 43694a23ef92..72c60ce662d8 100644 --- a/src/mondefense.cpp +++ b/src/mondefense.cpp @@ -58,14 +58,14 @@ void mdefense::zapback( monster &m, Creature *const source, if( const player *const foe = dynamic_cast( source ) ) { // Players/NPCs can avoid the shock if they wear non-conductive gear on their hands - for( const item &i : foe->worn ) { - if( ( i.covers( bp_hand_l ) || i.covers( bp_hand_r ) ) && - !i.conductive() && i.get_coverage() >= 95 ) { + for( const item * const &i : foe->worn ) { + if( ( i->covers( bp_hand_l ) || i->covers( bp_hand_r ) ) && + !i->conductive() && i->get_coverage() >= 95 ) { return; } } // Players/NPCs can avoid the shock by using non-conductive weapons - if( !foe->weapon.conductive() ) { + if( !foe->get_weapon().conductive() ) { if( foe->reach_attacking ) { return; } @@ -113,7 +113,7 @@ void mdefense::acidsplash( monster &m, Creature *const source, } } else { if( const player *const foe = dynamic_cast( source ) ) { - if( foe->weapon.is_melee( DT_CUT ) || foe->weapon.is_melee( DT_STAB ) ) { + if( foe->get_weapon().is_melee( DT_CUT ) || foe->get_weapon().is_melee( DT_STAB ) ) { num_drops += rng( 3, 4 ); } if( foe->unarmed_attack() ) { diff --git a/src/monexamine.cpp b/src/monexamine.cpp index 7ff1d0bb4a9c..fa170db00003 100644 --- a/src/monexamine.cpp +++ b/src/monexamine.cpp @@ -216,21 +216,21 @@ bool monexamine::pet_menu( monster &z ) const itype &type = *z.type->mech_battery; int max_charge = type.magazine->capacity; float charge_percent; - if( z.battery_item ) { - charge_percent = static_cast( z.battery_item->ammo_remaining() ) / max_charge * 100; + if( z.get_battery_item() ) { + charge_percent = static_cast( z.get_battery_item()->ammo_remaining() ) / max_charge * 100; } else { charge_percent = 0.0; } amenu.addentry( check_bat, false, 'c', _( "%s battery level is %d%%" ), z.get_name(), static_cast( charge_percent ) ); - if( you.weapon.is_null() && z.battery_item ) { + if( you.get_weapon().is_null() && z.get_battery_item() ) { amenu.addentry( mount, true, 'r', _( "Climb into the mech and take control" ) ); - } else if( !you.weapon.is_null() ) { + } else if( !you.get_weapon().is_null() ) { amenu.addentry( mount, false, 'r', _( "You cannot pilot the mech whilst wielding something" ) ); - } else if( !z.battery_item ) { + } else if( !z.get_battery_item() ) { amenu.addentry( mount, false, 'r', _( "This mech has a dead battery and won't turn on" ) ); } - if( z.battery_item ) { + if( z.get_battery_item() ) { amenu.addentry( remove_bat, true, 'x', _( "Remove the mech's battery pack" ) ); } else if( you.has_amount( z.type->mech_battery, 1 ) ) { amenu.addentry( insert_bat, true, 'x', _( "Insert a new battery pack" ) ); @@ -359,11 +359,11 @@ void monexamine::shear_animal( monster &z ) z.add_effect( effect_tied, 1_turns ); you.activity.str_values.push_back( "temp_tie" ); } - you.activity.targets.push_back( item_location( you, you.best_quality_item( qual_shear ) ) ); + you.activity.targets.push_back( you.best_quality_item( qual_shear ) ); add_msg( _( "You start shearing the %s." ), z.get_name() ); } -static item_location pet_armor_loc( monster &z ) +static item* pet_armor_loc( monster &z ) { auto filter = [z]( const item & it ) { return z.type->bodytype == it.get_pet_armor_bodytype() && @@ -374,7 +374,7 @@ static item_location pet_armor_loc( monster &z ) return game_menus::inv::titled_filter_menu( filter, get_avatar(), _( "Pet armor" ) ); } -static item_location tack_loc() +static item* tack_loc() { auto filter = []( const item & it ) { return it.has_flag( "TACK" ); @@ -385,13 +385,17 @@ static item_location tack_loc() void monexamine::remove_battery( monster &z ) { - get_map().add_item_or_charges( get_player_character().pos(), *z.battery_item ); - z.battery_item.reset(); + //TODO!: standard ownership + if(z.get_battery_item()){ + z.get_battery_item()->detach(); + } + get_map().add_item_or_charges( get_player_character().pos(), *z.get_battery_item() ); + } void monexamine::insert_battery( monster &z ) { - if( z.battery_item ) { + if( z.get_battery_item() ) { // already has a battery, shouldn't be called with one, but just incase. return; } @@ -407,7 +411,7 @@ void monexamine::insert_battery( monster &z ) selection_menu.text = string_format( _( "Select an battery to insert into your %s." ), z.get_name() ); selection_menu.addentry( i++, true, MENU_AUTOASSIGN, _( "Cancel" ) ); - for( auto iter : bat_inv ) { + for( auto &iter : bat_inv ) { selection_menu.addentry( i++, true, MENU_AUTOASSIGN, _( "Use %s" ), iter->tname() ); } selection_menu.selected = 1; @@ -420,8 +424,9 @@ void monexamine::insert_battery( monster &z ) item *bat_item = bat_inv[index - 1]; int item_pos = you.get_item_position( bat_item ); if( item_pos != INT_MIN ) { - z.battery_item = cata::make_value( *bat_item ); + //TODO!: check owner here you.i_rem( item_pos ); + z.set_battery_item(bat_item); } } @@ -545,20 +550,21 @@ bool monexamine::mfriend_menu( monster &z ) void monexamine::attach_or_remove_saddle( monster &z ) { + //TODO!: check ownership/null if( z.has_effect( effect_saddled ) ) { z.remove_effect( effect_saddled ); - get_avatar().i_add( *z.tack_item ); - z.tack_item.reset(); + z.get_tack_item()->detach(); + get_avatar().i_add( *z.get_tack_item() ); } else { - item_location loc = tack_loc(); + item* loc = tack_loc(); if( !loc ) { add_msg( _( "Never mind." ) ); return; } + loc->detach(); z.add_effect( effect_saddled, 1_turns, num_bp ); - z.tack_item = cata::make_value( *loc.get_item() ); - loc.remove_item(); + z.set_tack_item(loc); } } @@ -645,17 +651,18 @@ void monexamine::attach_bag_to( monster &z ) }; avatar &you = get_avatar(); - item_location loc = game_menus::inv::titled_filter_menu( filter, you, _( "Bag item" ) ); + item* loc = game_menus::inv::titled_filter_menu( filter, you, _( "Bag item" ) ); if( !loc ) { add_msg( _( "Never mind." ) ); return; } + //TODO!: check ownership item &it = *loc; - z.storage_item = cata::make_value( it ); - add_msg( _( "You mount the %1$s on your %2$s." ), it.display_name(), pet_name ); you.i_rem( &it ); + z.set_storage_item(&it); + add_msg( _( "You mount the %1$s on your %2$s." ), it.display_name(), pet_name ); z.add_effect( effect_has_bag, 1_turns, num_bp ); // Update encumbrance in case we were wearing it you.flag_encumbrance(); @@ -665,14 +672,15 @@ void monexamine::attach_bag_to( monster &z ) void monexamine::remove_bag_from( monster &z ) { std::string pet_name = z.get_name(); - if( z.storage_item ) { + if( z.get_storage_item() ) { if( !z.inv.empty() ) { dump_items( z ); } avatar &you = get_avatar(); - get_map().add_item_or_charges( you.pos(), *z.storage_item ); - add_msg( _( "You remove the %1$s from %2$s." ), z.storage_item->display_name(), pet_name ); - z.storage_item.reset(); + z.get_storage_item()->detach(); + get_map().add_item_or_charges( you.pos(), *z.get_storage_item() ); + add_msg( _( "You remove the %1$s from %2$s." ), z.get_storage_item()->display_name(), pet_name ); + //TODO!: check ownership shit again you.moves -= 200; } else { add_msg( m_bad, _( "Your %1$s doesn't have a bag!" ), pet_name ); @@ -686,7 +694,7 @@ void monexamine::dump_items( monster &z ) avatar &you = get_avatar(); map &here = get_map(); for( auto &it : z.inv ) { - here.add_item_or_charges( you.pos(), it ); + here.add_item_or_charges( you.pos(), *it ); } z.inv.clear(); add_msg( _( "You dump the contents of the %s's bag on the ground." ), pet_name ); @@ -696,21 +704,23 @@ void monexamine::dump_items( monster &z ) bool monexamine::give_items_to( monster &z ) { std::string pet_name = z.get_name(); - if( !z.storage_item ) { + if( !z.get_storage_item() ) { add_msg( _( "There is no container on your %s to put things in!" ), pet_name ); return true; } - item &storage = *z.storage_item; + item &storage = *z.get_storage_item(); units::mass max_weight = z.weight_capacity() - z.get_carried_weight(); units::volume max_volume = storage.get_storage() - z.get_carried_volume(); avatar &you = get_avatar(); + //TODO!: check this drop_locations items = game_menus::inv::multidrop( you ); drop_locations to_move; for( const drop_location &itq : items ) { - item it_copy = *itq.loc; + item &it_copy = *itq.loc; + int charges = -1; if( it_copy.count_by_charges() ) { - it_copy.charges = itq.count; + charges = itq.count; } units::volume item_volume = it_copy.volume(); units::mass item_weight = it_copy.weight(); @@ -735,7 +745,7 @@ bool monexamine::give_items_to( monster &z ) bool monexamine::add_armor( monster &z ) { std::string pet_name = z.get_name(); - item_location loc = pet_armor_loc( z ); + item* loc = pet_armor_loc( z ); if( !loc ) { add_msg( _( "Never mind." ) ); @@ -750,11 +760,11 @@ bool monexamine::add_armor( monster &z ) return true; } + loc->detach(); armor.set_var( "pet_armor", "true" ); - z.armor_item = cata::make_value( armor ); + z.set_armor_item(&armor); add_msg( pgettext( "pet armor", "You put the %1$s on your %2$s." ), armor.display_name(), pet_name ); - loc.remove_item(); z.add_effect( effect_monster_armor, 1_turns, num_bp ); // TODO: armoring a horse takes a lot longer than 2 seconds. This should be a long action. get_avatar().moves -= 200; @@ -770,12 +780,13 @@ void monexamine::remove_harness( monster &z ) void monexamine::remove_armor( monster &z ) { std::string pet_name = z.get_name(); - if( z.armor_item ) { - z.armor_item->erase_var( "pet_armor" ); - get_map().add_item_or_charges( z.pos(), *z.armor_item ); - add_msg( pgettext( "pet armor", "You remove the %1$s from %2$s." ), z.armor_item->display_name(), + if( z.get_armor_item() ) { + z.get_armor_item()->erase_var( "pet_armor" ); + item * armor=z.get_armor_item(); + armor->detach(); + get_map().add_item_or_charges( z.pos(), *armor ); + add_msg( pgettext( "pet armor", "You remove the %1$s from %2$s." ), armor->display_name(), pet_name ); - z.armor_item.reset(); // TODO: removing armor from a horse takes a lot longer than 2 seconds. This should be a long action. get_avatar().moves -= 200; } else { @@ -802,8 +813,9 @@ void monexamine::kill_zslave( monster &z ) if( !one_in( 3 ) ) { you.add_msg_if_player( _( "You tear out the pheromone ball from the zombie slave." ) ); - item ball( "pheromone", calendar::start_of_cataclysm ); - iuse::pheromone( &you, &ball, true, you.pos() ); + //TODO!: is this meant to be temp?? + item *ball=item::spawn_temporary( "pheromone", calendar::start_of_cataclysm ); + iuse::pheromone( &you, ball, true, you.pos() ); } } @@ -835,8 +847,9 @@ void monexamine::add_leash( monster &z ) return; } item *rope_item = rope_inv[index - 1]; - z.tied_item = cata::make_value( *rope_item ); + //TODO!: ownerships again player.i_rem( rope_item ); + z.set_tied_item(rope_item); z.add_effect( effect_leashed, 1_turns ); z.get_effect( effect_leashed ).set_permanent(); add_msg( _( "You add a leash to your %s." ), z.get_name() ); @@ -850,9 +863,12 @@ void monexamine::remove_leash( monster &z ) z.remove_effect( effect_led_by_leash ); z.remove_effect( effect_leashed ); - if( z.tied_item ) { - get_player_character().i_add( *z.tied_item ); - z.tied_item.reset(); + if( z.get_tied_item() ) { + //TODO!: more ownership + item * it=z.get_tied_item(); + it->detach(); + get_player_character().i_add( *it ); + } add_msg( _( "You remove the leash from your %s." ), z.get_name() ); } @@ -924,7 +940,7 @@ void monexamine::deactivate_pet( monster &z ) attach_or_remove_saddle( z ); } map &here = get_map(); - here.add_item_or_charges( z.pos(), z.to_item() ); + here.add_item_or_charges( z.pos(), *z.to_item() ); if( !z.has_flag( MF_INTERIOR_AMMO ) ) { for( auto &ammodef : z.ammo ) { if( ammodef.second > 0 ) { diff --git a/src/monmove.cpp b/src/monmove.cpp index f9844873a06a..baf751f5f930 100644 --- a/src/monmove.cpp +++ b/src/monmove.cpp @@ -704,7 +704,7 @@ void monster::move() } static const auto volume_per_hp = 250_ml; for( auto &elem : g->m.i_at( pos() ) ) { - hp += elem.volume() / volume_per_hp; // Yeah this means it can get more HP than normal. + hp += elem->volume() / volume_per_hp; // Yeah this means it can get more HP than normal. if( has_flag( MF_ABSORBS_SPLITS ) ) { while( hp / 2 > type->hp ) { monster *const spawn = g->place_critter_around( type->id, pos(), 1 ); @@ -1737,7 +1737,7 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter, if( one_in( 10 ) ) { // if it has more napalm, drop some and reduce ammo in tank if( ammo[itype_pressurized_tank] > 0 ) { - g->m.add_item_or_charges( pos(), item( "napalm", calendar::turn, 50 ) ); + g->m.add_item_or_charges( pos(), *item::spawn( "napalm", calendar::turn, 50 ) ); ammo[itype_pressurized_tank] -= 50; } else { // TODO: remove MF_DRIPS_NAPALM flag since no more napalm in tank @@ -1748,7 +1748,7 @@ bool monster::move_to( const tripoint &p, bool force, bool step_on_critter, if( has_flag( MF_DRIPS_GASOLINE ) ) { if( one_in( 5 ) ) { // TODO: use same idea that limits napalm dripping - g->m.add_item_or_charges( pos(), item( "gasoline" ) ); + g->m.add_item_or_charges( pos(), *item::spawn( "gasoline" ) ); } } return true; diff --git a/src/monster.cpp b/src/monster.cpp index 36d351909e62..89b99a7a5c84 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -166,6 +166,8 @@ static std::vector find_targets_to_ungrab( const tripoint &pos ) return result; } +//TODO!: make sure all the uses of tack_item etc are properly locationd + monster::monster() { position.x = 20; @@ -217,9 +219,9 @@ monster::monster( const mtype_id &id ) : monster() if( monster::has_flag( MF_RIDEABLE_MECH ) ) { itype_id mech_bat = itype_id( type->mech_battery ); int max_charge = mech_bat->magazine->capacity; - item mech_bat_item = item( mech_bat, calendar::start_of_cataclysm ); - mech_bat_item.ammo_consume( rng( 0, max_charge ), tripoint_zero ); - battery_item = cata::make_value( mech_bat_item ); + item *mech_bat_item = item::spawn( mech_bat, calendar::start_of_cataclysm ); + mech_bat_item->ammo_consume( rng( 0, max_charge ), tripoint_zero ); + set_battery_item(mech_bat_item); } } @@ -439,7 +441,7 @@ void monster::try_reproduce() if( type->baby_monster ) { g->m.add_spawn( type->baby_monster, spawn_cnt, pos() ); } else { - g->m.add_item_or_charges( pos(), item( type->baby_egg, *baby_timer, spawn_cnt ), true ); + g->m.add_item_or_charges( pos(), *item::spawn( type->baby_egg, *baby_timer, spawn_cnt ), true ); } } @@ -523,9 +525,9 @@ std::string monster::name_with_armor() const ret = _( "armor" ); } if( has_effect( effect_monster_armor ) && !inv.empty() ) { - for( const item &armor : inv ) { - if( armor.is_pet_armor( true ) ) { - ret += string_format( _( "wearing %1$s" ), armor.tname( 1 ) ); + for( const item * const &armor : inv ) { + if( armor->is_pet_armor( true ) ) { + ret += string_format( _( "wearing %1$s" ), armor->tname( 1 ) ); break; } } @@ -808,7 +810,7 @@ std::string monster::extended_description() const ss += "--\n"; ss += std::string( _( "In melee, you can expect to:" ) ) + "\n"; ss += string_format( _( "Deal average damage per second: %.1f" ), - g->u.weapon.effective_dps( g->u, *this ) ); + g->u.get_weapon().effective_dps( g->u, *this ) ); ss += "\n"; if( debug_mode ) { @@ -1711,17 +1713,23 @@ bool monster::move_effects( bool ) if( u_see_me ) { add_msg( _( "The %s easily slips out of its bonds." ), name() ); } - g->m.add_item_or_charges( pos(), *tied_item ); - tied_item.reset(); + //TODO!: ownerships + item * it= get_tied_item(); + it->detach(); + g->m.add_item_or_charges( pos(), *it ); } } else { if( tied_item ) { + item * it= get_tied_item(); + const bool broken = rng( type->melee_dice * type->melee_sides, std::min( 10000, type->melee_dice * type->melee_sides * 250 ) ) > 800; + it->detach(); if( !broken ) { - g->m.add_item_or_charges( pos(), *tied_item ); - } - tied_item.reset(); + g->m.add_item_or_charges( pos(), *it ); + }else{ + it->destroy(); + } if( u_see_me ) { if( broken ) { add_msg( _( "The %s snaps the bindings holding it down." ), name() ); @@ -2216,7 +2224,7 @@ void monster::process_turn() const bool player_sees = g->u.sees( zap ); const auto items = g->m.i_at( zap ); for( const auto &item : items ) { - if( item.made_of( LIQUID ) && item.flammable() ) { // start a fire! + if( item->made_of( LIQUID ) && item->flammable() ) { // start a fire! g->m.add_field( zap, fd_fire, 2, 1_minutes ); sounds::sound( pos(), 30, sounds::sound_t::combat, _( "fwoosh!" ), false, "fire", "ignition" ); break; @@ -2309,15 +2317,15 @@ void monster::die( Creature *nkiller ) move_special_item_to_inv( tied_item ); if( has_effect( effect_lightsnare ) ) { - add_item( item( "string_36", calendar::start_of_cataclysm ) ); - add_item( item( "snare_trigger", calendar::start_of_cataclysm ) ); + add_item( *item::spawn( "string_36", calendar::start_of_cataclysm ) ); + add_item( *item::spawn( "snare_trigger", calendar::start_of_cataclysm ) ); } if( has_effect( effect_heavysnare ) ) { - add_item( item( "rope_6", calendar::start_of_cataclysm ) ); - add_item( item( "snare_trigger", calendar::start_of_cataclysm ) ); + add_item( *item::spawn( "rope_6", calendar::start_of_cataclysm ) ); + add_item( *item::spawn( "snare_trigger", calendar::start_of_cataclysm ) ); } if( has_effect( effect_beartrap ) ) { - add_item( item( "beartrap", calendar::start_of_cataclysm ) ); + add_item( *item::spawn( "beartrap", calendar::start_of_cataclysm ) ); } if( has_effect( effect_grabbing ) ) { remove_effect( effect_grabbing ); @@ -2329,7 +2337,7 @@ void monster::die( Creature *nkiller ) } if( !is_hallucination() ) { for( const auto &it : inv ) { - g->m.add_item_or_charges( pos(), it ); + g->m.add_item_or_charges( pos(), *it ); } } @@ -2420,18 +2428,19 @@ bool monster::check_mech_powered() const return true; } -static void process_item_valptr( cata::value_ptr &ptr, monster &mon ) +static void process_item_valptr( item* ptr, monster &mon ) { if( ptr && ptr->needs_processing() && ptr->process( nullptr, mon.pos(), false ) ) { - ptr.reset(); + ptr->detach(); + ptr->destroy(); } } void monster::process_items() { for( auto iter = inv.begin(); iter != inv.end(); ) { - if( iter->needs_processing() && - iter->process( nullptr, pos(), false ) + if( ( *iter )->needs_processing() && + ( *iter )->process( nullptr, pos(), false ) ) { iter = inv.erase( iter ); continue; @@ -2454,14 +2463,15 @@ void monster::drop_items_on_death() return; } - std::vector items = item_group::items_from( type->death_drops, calendar::start_of_cataclysm ); + std::vector items = item_group::items_from( type->death_drops, + calendar::start_of_cataclysm ); // This block removes some items, according to item spawn scaling factor const float spawn_rate = get_option( "ITEM_SPAWNRATE" ); if( spawn_rate < 1 ) { // Temporary vector, to remember which items will be dropped - std::vector remaining; - for( const item &it : items ) { + std::vector remaining; + for( item *&it : items ) { if( rng_float( 0, 1 ) < spawn_rate ) { remaining.push_back( it ); } @@ -2664,9 +2674,9 @@ void monster::make_ally( const monster &z ) faction = z.faction; } -void monster::add_item( const item &it ) +void monster::add_item( item &it ) { - inv.push_back( it ); + inv.push_back( &it ); } bool monster::is_hallucination() const @@ -2751,8 +2761,8 @@ units::mass monster::get_carried_weight() if( armor_item ) { total_weight += armor_item->weight(); } - for( const item &it : inv ) { - total_weight += it.weight(); + for( const item * const &it : inv ) { + total_weight += it->weight(); } return total_weight; } @@ -2760,17 +2770,17 @@ units::mass monster::get_carried_weight() units::volume monster::get_carried_volume() { units::volume total_volume = 0_ml; - for( const item &it : inv ) { - total_volume += it.volume(); + for( const item * const &it : inv ) { + total_volume += it->volume(); } return total_volume; } -void monster::move_special_item_to_inv( cata::value_ptr &it ) +void monster::move_special_item_to_inv( item* it ) { if( it ) { + it->detach(); add_item( *it ); - it.reset(); } } @@ -2809,15 +2819,15 @@ void monster::init_from_item( const item &itm ) } } -item monster::to_item() const +item *monster::to_item() const { if( type->revert_to_itype.is_empty() ) { - return item(); + return &null_item_reference(); } // Birthday is wrong, but the item created here does not use it anyway (I hope). - item result( type->revert_to_itype, calendar::turn ); - const int damfac = std::max( 1, ( result.max_damage() + 1 ) * hp / type->hp ); - result.set_damage( std::max( 0, ( result.max_damage() + 1 ) - damfac ) ); + item *result = item::spawn( type->revert_to_itype, calendar::turn ); + const int damfac = std::max( 1, ( result->max_damage() + 1 ) * hp / type->hp ); + result->set_damage( std::max( 0, ( result->max_damage() + 1 ) - damfac ) ); return result; } diff --git a/src/monster.h b/src/monster.h index c77ee9c566db..f8e21624ad81 100644 --- a/src/monster.h +++ b/src/monster.h @@ -423,7 +423,7 @@ class monster : public Creature, public visitable /** Makes this monster an ally of the given monster. */ void make_ally( const monster &z ); // Add an item to inventory - void add_item( const item &it ); + void add_item( item &it ); // check mech power levels and modify it. bool use_mech_power( int amt ); bool check_mech_powered() const; @@ -456,19 +456,14 @@ class monster : public Creature, public visitable // TEMP VALUES tripoint wander_pos; // Wander destination - Just try to move in that direction int wandf; // Urge to wander - Increased by sound, decrements each move - std::vector inv; // Inventory - std::vector corpse_components; // Hack to make bionic corpses generate CBMs on death + std::vector inv; // Inventory + std::vector corpse_components; // Hack to make bionic corpses generate CBMs on death Character *mounted_player = nullptr; // player that is mounting this creature character_id mounted_player_id; // id of player that is mounting this creature ( for save/load ) character_id dragged_foe_id; // id of character being dragged by the monster - cata::value_ptr tied_item; // item used to tie the monster - cata::value_ptr tack_item; // item representing saddle and reins and such - cata::value_ptr armor_item; // item of armor the monster may be wearing - cata::value_ptr storage_item; // storage item for monster carrying items - cata::value_ptr battery_item; // item to power mechs units::mass get_carried_weight(); units::volume get_carried_volume(); - void move_special_item_to_inv( cata::value_ptr &it ); + void move_special_item_to_inv( item *it ); // DEFINING VALUES int friendly; @@ -519,7 +514,7 @@ class monster : public Creature, public visitable * Only useful for robots and the like, the monster must have at least * a non-empty item id as revert_to_itype. */ - item to_item() const; + item *to_item() const; /** * Initialize values like speed / hp from data of an item. * This applies to robotic monsters that are spawned by invoking an item (e.g. turret), @@ -546,6 +541,21 @@ class monster : public Creature, public visitable void set_summon_time( const time_duration &length ); // handles removing the monster if the timer runs out void decrement_summon_timer(); + + item* get_tack_item() const; + void set_tack_item(item* to); + + item* get_tied_item() const; + void set_tied_item(item* to); + + item* get_armor_item() const; + void set_armor_item(item* to); + + item* get_storage_item() const; + void set_storage_item(item* to); + + item* get_battery_item() const; + void set_battery_item(item* to); private: void process_trigger( mon_trigger trig, int amount ); void process_trigger( mon_trigger trig, const std::function &amount_func ); @@ -575,6 +585,11 @@ class monster : public Creature, public visitable void nursebot_operate( player *dragged_foe ); protected: + item *tied_item; // item used to tie the monster + item *tack_item; // item representing saddle and reins and such + item *armor_item; // item of armor the monster may be wearing + item *storage_item; // storage item for monster carrying items + item *battery_item; // item to power mechs void store( JsonOut &json ) const; void load( const JsonObject &data ); diff --git a/src/monstergenerator.cpp b/src/monstergenerator.cpp index 65d3e51e1e79..c64cfc2790ff 100644 --- a/src/monstergenerator.cpp +++ b/src/monstergenerator.cpp @@ -1207,8 +1207,9 @@ void MonsterGenerator::check_monster_definitions() const if( mon.has_flag( MF_MILKABLE ) && mon.starting_ammo.empty() ) { debugmsg( "monster %s is flagged milkable, but has no starting ammo", mon.id.c_str() ); } + //TODO!: move temporary upwards if( mon.has_flag( MF_MILKABLE ) && !mon.starting_ammo.empty() && - !item( mon.starting_ammo.begin()->first ).made_of( LIQUID ) ) { + !item::spawn_temporary( mon.starting_ammo.begin()->first )->made_of( LIQUID ) ) { debugmsg( "monster % is flagged milkable, but starting ammo %s is not a liquid type", mon.id.c_str(), mon.starting_ammo.begin()->first.str() ); } diff --git a/src/morale.cpp b/src/morale.cpp index bd37b612b33e..6106c2db9cef 100644 --- a/src/morale.cpp +++ b/src/morale.cpp @@ -942,12 +942,6 @@ void player_morale::on_item_takeoff( const item &it ) set_worn( it, false ); } -void player_morale::on_worn_item_transform( const item &old_it, const item &new_it ) -{ - set_worn( old_it, false ); - set_worn( new_it, true ); -} - void player_morale::on_worn_item_washed( const item &it ) { const auto update_body_part = [&]( body_part_data & bp_data ) { diff --git a/src/morale.h b/src/morale.h index 25b78a7094b2..056456ae55ed 100644 --- a/src/morale.h +++ b/src/morale.h @@ -86,7 +86,6 @@ class player_morale void on_stat_change( const std::string &stat, int value ); void on_item_wear( const item &it ); void on_item_takeoff( const item &it ); - void on_worn_item_transform( const item &old_it, const item &new_it ); void on_worn_item_washed( const item &it ); void on_effect_int_change( const efftype_id &eid, int intensity, const bodypart_str_id &bp = bodypart_str_id::NULL_ID() ); diff --git a/src/mutation.cpp b/src/mutation.cpp index 472de7b7d4d8..5873ded9cc59 100644 --- a/src/mutation.cpp +++ b/src/mutation.cpp @@ -495,8 +495,8 @@ void Character::activate_mutation( const trait_id &mut ) add_msg_if_player( _( "You start spinning web with your spinnerets!" ) ); } else if( mut == trait_BURROW ) { tdata.powered = false; - item burrowing_item( itype_id( "fake_burrowing" ) ); - invoke_item( &burrowing_item ); + item* burrowing_item=item::spawn_temporary( itype_id( "fake_burrowing" ) ); + invoke_item( burrowing_item ); return; // handled when the activity finishes } else if( mut == trait_SLIMESPAWNER ) { monster *const slime = g->place_critter_around( mtype_id( "mon_player_blob" ), pos(), 1 ); @@ -594,14 +594,15 @@ void Character::activate_mutation( const trait_id &mut ) } return; } else if( !mdata.spawn_item.is_empty() ) { - item tmpitem( mdata.spawn_item ); - i_add_or_drop( tmpitem ); + item *tmpitem=item::spawn( mdata.spawn_item ); + i_add_or_drop( *tmpitem ); add_msg_if_player( mdata.spawn_item_message() ); tdata.powered = false; return; } else if( !mdata.ranged_mutation.is_empty() ) { add_msg_if_player( mdata.ranged_mutation_message() ); - avatar_action::fire_ranged_mutation( g->u, item( mdata.ranged_mutation ) ); + //TODO!: checkssss + avatar_action::fire_ranged_mutation( g->u, *item::spawn( mdata.ranged_mutation ) ); tdata.powered = false; return; } @@ -1520,7 +1521,7 @@ static mutagen_rejection try_reject_mutagen( Character &guy, const item &it, boo "MYCUS", "MARLOSS", "MARLOSS_SEED", "MARLOSS_GEL" } }; - if( std::any_of( safe.begin(), safe.end(), [it]( const std::string & flag ) { + if( std::any_of( safe.begin(), safe.end(), [&it]( const std::string & flag ) { return it.type->can_use( flag ); } ) ) { return mutagen_rejection::accepted; diff --git a/src/newcharacter.cpp b/src/newcharacter.cpp index b20cedad57ac..56ae4943d4a8 100644 --- a/src/newcharacter.cpp +++ b/src/newcharacter.cpp @@ -399,7 +399,7 @@ void avatar::randomize( const bool random_scenario, points_left &points, bool pl bool avatar::create( character_type type, const std::string &tempname ) { - weapon = item( "null", calendar::start_of_cataclysm ); + set_weapon(null_item_reference()); prof = profession::generic(); g->scen = scenario::generic(); @@ -530,7 +530,7 @@ bool avatar::create( character_type type, const std::string &tempname ) scent = 300; } - weapon = item( "null", calendar::start_of_cataclysm ); + set_weapon(null_item_reference()); // Grab the skills from the profession, if there are any // We want to do this before the recipes @@ -567,32 +567,32 @@ bool avatar::create( character_type type, const std::string &tempname ) starting_vehicle = prof->vehicle(); } - std::list prof_items = prof->items( male, get_mutations() ); + ItemList prof_items = prof->items( male, get_mutations() ); - for( item &it : prof_items ) { - if( it.has_flag( flag_WET ) ) { - it.active = true; - it.item_counter = 450; // Give it some time to dry off + for( item * const &it : prof_items ) { + if( it->has_flag( flag_WET ) ) { + it->active = true; + it->item_counter = 450; // Give it some time to dry off } // TODO: debugmsg if food that isn't a seed is inedible - if( it.has_flag( "no_auto_equip" ) ) { - it.unset_flag( "no_auto_equip" ); - inv.push_back( it ); - } else if( it.has_flag( "auto_wield" ) ) { - it.unset_flag( "auto_wield" ); + if( it->has_flag( "no_auto_equip" ) ) { + it->unset_flag( "no_auto_equip" ); + inv.push_back( *it ); + } else if( it->has_flag( "auto_wield" ) ) { + it->unset_flag( "auto_wield" ); if( !is_armed() ) { - wield( it ); + wield( *it ); } else { - inv.push_back( it ); + inv.push_back( *it ); } - } else if( it.is_armor() ) { + } else if( it->is_armor() ) { // TODO: debugmsg if wearing fails - wear_item( it, false ); + wear_item( *it, false ); } else { - inv.push_back( it ); + inv.push_back( *it ); } - if( it.is_book() ) { - items_identified.insert( it.typeId() ); + if( it->is_book() ) { + items_identified.insert( it->typeId() ); } } @@ -1542,14 +1542,14 @@ tab_direction set_profession( avatar &u, points_left &points, std::string buffer_worn; std::string buffer_inventory; for( const auto &it : prof_items ) { - if( it.has_flag( "no_auto_equip" ) ) { - buffer_inventory += it.display_name() + "\n"; - } else if( it.has_flag( "auto_wield" ) ) { - buffer_wielded += it.display_name() + "\n"; - } else if( it.is_armor() ) { - buffer_worn += it.display_name() + "\n"; + if( it->has_flag( "no_auto_equip" ) ) { + buffer_inventory += it->display_name() + "\n"; + } else if( it->has_flag( "auto_wield" ) ) { + buffer_wielded += it->display_name() + "\n"; + } else if( it->is_armor() ) { + buffer_worn += it->display_name() + "\n"; } else { - buffer_inventory += it.display_name() + "\n"; + buffer_inventory += it->display_name() + "\n"; } } buffer += colorize( _( "Wielded:" ), c_cyan ) + "\n"; @@ -2978,7 +2978,7 @@ cata::optional query_for_template_name() return spop.text(); } } - +//TODO!: check the serialization here void avatar::save_template( const std::string &name, const points_left &points ) { std::string name_san = ensure_valid_file_name( name ); diff --git a/src/npc.cpp b/src/npc.cpp index 41948907ccff..df27b36341db 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -191,12 +191,12 @@ standard_npc::standard_npc( const std::string &name, const tripoint &pos, } for( const std::string &e : clothing ) { - wear_item( item( e ) ); + wear_item( *item::spawn( e ) ); } - for( item &e : worn ) { - if( e.has_flag( "VARSIZE" ) ) { - e.set_flag( "FIT" ); + for( item * const &e : worn ) { + if( e->has_flag( "VARSIZE" ) ) { + e->set_flag( "FIT" ); } } } @@ -320,7 +320,7 @@ void npc::randomize( const npc_class_id &type ) setID( g->assign_npc_id() ); } - weapon = item( "null", calendar::start_of_cataclysm ); + set_weapon(null_item_reference()); inv.clear(); personality.aggression = rng( -10, 10 ); personality.bravery = rng( -3, 10 ); @@ -502,105 +502,106 @@ faction *npc::get_faction() const // item id from group "_" or from fallback group // may still be a null item! -static item random_item_from( const npc_class_id &type, const std::string &what, - const item_group_id &fallback ) +static item &random_item_from( const npc_class_id &type, const std::string &what, + const item_group_id &fallback ) { auto result = item_group::item_from( item_group_id( type.str() + "_" + what ), calendar::turn ); - if( result.is_null() ) { + if( result->is_null() ) { result = item_group::item_from( fallback, calendar::turn ); } - return result; + return *result; } // item id from "_" or from "npc_" -static item random_item_from( const npc_class_id &type, const std::string &what ) +static item &random_item_from( const npc_class_id &type, const std::string &what ) { return random_item_from( type, what, item_group_id( "npc_" + what ) ); } // item id from "__" or from "npc__" -static item get_clothing_item( const npc_class_id &type, const std::string &what, bool male ) +static item &get_clothing_item( const npc_class_id &type, const std::string &what, bool male ) { - item result; + item *result; //Check if class has gendered clothing //Then check if it has an ungendered version //Only if all that fails, grab from the default class. if( male ) { - result = random_item_from( type, what + "_male", item_group_id::NULL_ID() ); + result = &random_item_from( type, what + "_male", item_group_id::NULL_ID() ); } else { - result = random_item_from( type, what + "_female", item_group_id::NULL_ID() ); + result = &random_item_from( type, what + "_female", item_group_id::NULL_ID() ); } - if( result.is_null() ) { + if( result->is_null() ) { if( male ) { - result = random_item_from( type, what, item_group_id( "npc_" + what + "_male" ) ); + result = &random_item_from( type, what, item_group_id( "npc_" + what + "_male" ) ); } else { - result = random_item_from( type, what, item_group_id( "npc_" + what + "_female" ) ); + result = &random_item_from( type, what, item_group_id( "npc_" + what + "_female" ) ); } } - return result; + return *result; } void starting_clothes( npc &who, const npc_class_id &type, bool male ) { - std::vector ret; + std::vector ret; if( item_group::group_is_defined( type->worn_override ) ) { ret = item_group::items_from( type->worn_override ); } else { - ret.push_back( get_clothing_item( type, "pants", male ) ); - ret.push_back( get_clothing_item( type, "shirt", male ) ); - ret.push_back( get_clothing_item( type, "underwear_top", male ) ); - ret.push_back( get_clothing_item( type, "underwear_bottom", male ) ); - ret.push_back( get_clothing_item( type, "underwear_feet", male ) ); - ret.push_back( get_clothing_item( type, "shoes", male ) ); - ret.push_back( random_item_from( type, "gloves" ) ); - ret.push_back( random_item_from( type, "coat" ) ); - ret.push_back( random_item_from( type, "vest" ) ); - ret.push_back( random_item_from( type, "masks" ) ); + ret.push_back( &get_clothing_item( type, "pants", male ) ); + ret.push_back( &get_clothing_item( type, "shirt", male ) ); + ret.push_back( &get_clothing_item( type, "underwear_top", male ) ); + ret.push_back( &get_clothing_item( type, "underwear_bottom", male ) ); + ret.push_back( &get_clothing_item( type, "underwear_feet", male ) ); + ret.push_back( &get_clothing_item( type, "shoes", male ) ); + ret.push_back( &random_item_from( type, "gloves" ) ); + ret.push_back( &random_item_from( type, "coat" ) ); + ret.push_back( &random_item_from( type, "vest" ) ); + ret.push_back( &random_item_from( type, "masks" ) ); // Why is the alternative group not named "npc_glasses" but "npc_eyes"? - ret.push_back( random_item_from( type, "glasses", item_group_id( "npc_eyes" ) ) ); - ret.push_back( random_item_from( type, "hat" ) ); - ret.push_back( random_item_from( type, "scarf" ) ); - ret.push_back( random_item_from( type, "storage" ) ); - ret.push_back( random_item_from( type, "holster" ) ); - ret.push_back( random_item_from( type, "belt" ) ); - ret.push_back( random_item_from( type, "wrist" ) ); - ret.push_back( random_item_from( type, "extra" ) ); + ret.push_back( &random_item_from( type, "glasses", item_group_id( "npc_eyes" ) ) ); + ret.push_back( &random_item_from( type, "hat" ) ); + ret.push_back( &random_item_from( type, "scarf" ) ); + ret.push_back( &random_item_from( type, "storage" ) ); + ret.push_back( &random_item_from( type, "holster" ) ); + ret.push_back( &random_item_from( type, "belt" ) ); + ret.push_back( &random_item_from( type, "wrist" ) ); + ret.push_back( &random_item_from( type, "extra" ) ); } - for( item &it : who.worn ) { - it.on_takeoff( who ); + for( item *&it : who.worn ) { + it->on_takeoff( who ); } who.worn.clear(); - for( item &it : ret ) { - if( it.has_flag( "VARSIZE" ) ) { - it.set_flag( "FIT" ); + for( item *&it : ret ) { + if( it->has_flag( "VARSIZE" ) ) { + it->set_flag( "FIT" ); } - if( who.can_wear( it ).success() ) { - it.on_wear( who ); + if( who.can_wear( *it ).success() ) { + it->on_wear( who ); who.worn.push_back( it ); - it.set_owner( who ); + it->set_owner( who ); } } } void starting_inv( npc &who, const npc_class_id &type ) { - std::list res; + ItemList res; who.inv.clear(); if( item_group::group_is_defined( type->carry_override ) ) { who.inv += item_group::items_from( type->carry_override ); return; } - res.emplace_back( "lighter" ); + res.emplace_back( item::spawn( "lighter" ) ); // If wielding a gun, get some additional ammo for it - if( who.weapon.is_gun() ) { - item ammo( who.weapon.ammo_default() ); - ammo = ammo.in_its_container(); - if( ammo.made_of( LIQUID ) ) { - item container( "bottle_plastic" ); - container.put_in( ammo ); + if( who.get_weapon().is_gun() ) { + //TODO!: checks + item *ammo = item::spawn( who.get_weapon().ammo_default() ); + ammo = &ammo->in_its_container(); + if( ammo->made_of( LIQUID ) ) { + item *container = item::spawn( "bottle_plastic" ); + container->put_in( *ammo ); ammo = container; } @@ -610,14 +611,14 @@ void starting_inv( npc &who, const npc_class_id &type ) type == NC_BOUNTY_HUNTER ); qty = rng( qty, qty * 2 ); - while( qty-- != 0 && who.can_pick_volume( ammo ) ) { + while( qty-- != 0 && who.can_pick_volume( *ammo ) ) { // TODO: give NPC a default magazine instead res.push_back( ammo ); } } if( type == NC_ARSONIST ) { - res.emplace_back( "molotov" ); + res.emplace_back( item::spawn( "molotov" ) ); } int qty = ( type == NC_EVAC_SHOPKEEP || @@ -625,22 +626,22 @@ void starting_inv( npc &who, const npc_class_id &type ) qty = rng( qty, qty * 3 ); while( qty-- != 0 ) { - item tmp = random_item_from( type, "misc" ).in_its_container(); - if( !tmp.is_null() ) { - if( !one_in( 3 ) && tmp.has_flag( "VARSIZE" ) ) { - tmp.set_flag( "FIT" ); + item *tmp = &random_item_from( type, "misc" ).in_its_container(); + if( !tmp->is_null() ) { + if( !one_in( 3 ) && tmp->has_flag( "VARSIZE" ) ) { + tmp->set_flag( "FIT" ); } - if( who.can_pick_volume( tmp ) ) { + if( who.can_pick_volume( *tmp ) ) { res.push_back( tmp ); } } } - res.erase( std::remove_if( res.begin(), res.end(), [&]( const item & e ) { - return e.has_flag( "TRADER_AVOID" ); + res.erase( std::remove_if( res.begin(), res.end(), [&]( const item * const & e ) { + return e->has_flag( "TRADER_AVOID" ); } ), res.end() ); for( auto &it : res ) { - it.set_owner( who ); + it->set_owner( who ); } who.inv += res; } @@ -803,7 +804,7 @@ int npc::best_skill_level() const void npc::starting_weapon( const npc_class_id &type ) { if( item_group::group_is_defined( type->weapon_override ) ) { - weapon = item_group::item_from( type->weapon_override, calendar::turn ); + set_weapon(*item_group::item_from( type->weapon_override, calendar::turn )); return; } @@ -811,29 +812,29 @@ void npc::starting_weapon( const npc_class_id &type ) // if NPC has no suitable skills default to stabbing weapon if( !best || best == skill_stabbing ) { - weapon = random_item_from( type, "stabbing", item_group_id( "survivor_stabbing" ) ); + set_weapon(random_item_from( type, "stabbing", item_group_id( "survivor_stabbing" ) )); } else if( best == skill_bashing ) { - weapon = random_item_from( type, "bashing", item_group_id( "survivor_bashing" ) ); + set_weapon(random_item_from( type, "bashing", item_group_id( "survivor_bashing" ) )); } else if( best == skill_cutting ) { - weapon = random_item_from( type, "cutting", item_group_id( "survivor_cutting" ) ); + set_weapon(random_item_from( type, "cutting", item_group_id( "survivor_cutting" ) )); } else if( best == skill_throw ) { - weapon = random_item_from( type, "throw" ); + set_weapon(random_item_from( type, "throw" )); } else if( best == skill_archery ) { - weapon = random_item_from( type, "archery" ); + set_weapon(random_item_from( type, "archery" )); } else if( best == skill_pistol ) { - weapon = random_item_from( type, "pistol", item_group_id( "guns_pistol_common" ) ); + set_weapon(random_item_from( type, "pistol", item_group_id( "guns_pistol_common" ) )); } else if( best == skill_shotgun ) { - weapon = random_item_from( type, "shotgun", item_group_id( "guns_shotgun_common" ) ); + set_weapon(random_item_from( type, "shotgun", item_group_id( "guns_shotgun_common" ) )); } else if( best == skill_smg ) { - weapon = random_item_from( type, "smg", item_group_id( "guns_smg_common" ) ); + set_weapon(random_item_from( type, "smg", item_group_id( "guns_smg_common" ) )); } else if( best == skill_rifle ) { - weapon = random_item_from( type, "rifle", item_group_id( "guns_rifle_common" ) ); + set_weapon(random_item_from( type, "rifle", item_group_id( "guns_rifle_common" ) )); } - if( weapon.is_gun() ) { - weapon.ammo_set( weapon.ammo_default() ); + if( get_weapon().is_gun() ) { + get_weapon().ammo_set( get_weapon().ammo_default() ); } - weapon.set_owner( get_faction()->id ); + get_weapon().set_owner( get_faction()->id ); } bool npc::can_read( const item &book, std::vector &fail_reasons ) @@ -895,7 +896,7 @@ int npc::time_to_read( const item &book, const player &reader ) const return retval; } -void npc::finish_read( item_location loc ) +void npc::finish_read( item* loc ) { if( !loc ) { revert_after_activity(); @@ -989,7 +990,7 @@ void npc::finish_read( item_location loc ) activity.set_to_null(); player *pl = dynamic_cast( this ); if( pl ) { - start_read( loc, pl ); + start_read( *loc, pl ); } if( activity ) { return; @@ -999,9 +1000,9 @@ void npc::finish_read( item_location loc ) revert_after_activity(); } -void npc::start_read( item_location loc, player *pl ) +void npc::start_read( item& loc, player *pl ) { - item &chosen = *loc; + item &chosen = loc; const int time_taken = time_to_read( chosen, *pl ); const double penalty = static_cast( time_taken ) / time_to_read( chosen, *pl ); player_activity act( ACT_READ, time_taken, 0, pl->getID().get_value() ); @@ -1034,7 +1035,7 @@ void npc::do_npc_read() if( g->u.sees( pos() ) ) { add_msg( m_info, _( "%s starts reading." ), disp_name() ); } - start_read( loc, pl ); + start_read( *loc, pl ); } else { for( const auto &elem : fail_reasons ) { say( elem ); @@ -1045,7 +1046,7 @@ void npc::do_npc_read() } } -bool npc::wear_if_wanted( const item &it, std::string &reason ) +bool npc::wear_if_wanted( item &it, std::string &reason ) { // Note: this function isn't good enough to use with NPC AI alone // Restrict it to player's orders for now @@ -1090,11 +1091,11 @@ bool npc::wear_if_wanted( const item &it, std::string &reason ) continue; } // Find an item that covers the same body part as the new item - auto iter = std::find_if( worn.begin(), worn.end(), [bp]( const item & armor ) { - return armor.covers( bp ); + auto iter = std::find_if( worn.begin(), worn.end(), [bp]( const item * const & armor ) { + return armor->covers( bp ); } ); - if( iter != worn.end() && !( is_limb_broken( bp ) && iter->has_flag( "SPLINT" ) ) ) { - took_off = takeoff( *iter ); + if( iter != worn.end() && !( is_limb_broken( bp ) && ( *iter )->has_flag( "SPLINT" ) ) ) { + took_off = takeoff( **iter ); break; } } @@ -1111,10 +1112,11 @@ bool npc::wear_if_wanted( const item &it, std::string &reason ) void npc::stow_item( item &it ) { - if( wear_item( weapon, false ) ) { + //TODO!: check locations + if( wear_item( get_weapon(), false ) ) { // Wearing the item was successful, remove weapon and post message. if( g->u.sees( pos() ) ) { - add_msg_if_npc( m_info, _( " wears the %s." ), weapon.tname() ); + add_msg_if_npc( m_info, _( " wears the %s." ), get_weapon().tname() ); } remove_weapon(); moves -= 15; @@ -1123,26 +1125,27 @@ void npc::stow_item( item &it ) return; } for( auto &e : worn ) { - if( e.can_holster( it ) ) { + if( e->can_holster( it ) ) { if( g->u.sees( pos() ) ) { //~ %1$s: weapon name, %2$s: holster name add_msg_if_npc( m_info, _( " puts away the %1$s in the %2$s." ), - weapon.tname(), e.tname() ); + get_weapon().tname(), e->tname() ); } - auto ptr = dynamic_cast( e.type->get_use( "holster" )->get_actor_ptr() ); - ptr->store( *this, e, it ); + //TODO!: check + auto ptr = dynamic_cast( e->type->get_use( "holster" )->get_actor_ptr() ); + ptr->store( *this, *e, it ); return; } } - if( volume_carried() + weapon.volume() <= volume_capacity() ) { + if( volume_carried() + get_weapon().volume() <= volume_capacity() ) { if( g->u.sees( pos() ) ) { - add_msg_if_npc( m_info, _( " puts away the %s." ), weapon.tname() ); + add_msg_if_npc( m_info, _( " puts away the %s." ), get_weapon().tname() ); } i_add( remove_weapon() ); moves -= 15; } else { // No room for weapon, so we drop it if( g->u.sees( pos() ) ) { - add_msg_if_npc( m_info, _( " drops the %s." ), weapon.tname() ); + add_msg_if_npc( m_info, _( " drops the %s." ), get_weapon().tname() ); } g->m.add_item_or_charges( pos(), remove_weapon() ); } @@ -1152,11 +1155,11 @@ bool npc::wield( item &it ) { cached_info.erase( "weapon_value" ); if( is_armed() ) { - stow_item( weapon ); + stow_item( get_weapon() ); } if( it.is_null() ) { - weapon = item(); + set_weapon(null_item_reference()); return true; } @@ -1180,13 +1183,13 @@ bool npc::wield( item &it ) moves -= 15; if( has_item( it ) ) { - weapon = remove_item( it ); + set_weapon(remove_item( it )); } else { - weapon = it; + set_weapon(it); } if( g->u.sees( pos() ) ) { - add_msg_if_npc( m_info, _( " wields a %s." ), weapon.tname() ); + add_msg_if_npc( m_info, _( " wields a %s." ), get_weapon().tname() ); } invalidate_range_cache(); return true; @@ -1203,24 +1206,24 @@ void npc::drop( const drop_locations &what, const tripoint &target, void npc::invalidate_range_cache() { - if( weapon.is_gun() ) { - confident_range_cache = confident_shoot_range( weapon, get_most_accurate_sight( weapon ) ); + if( get_weapon().is_gun() ) { + confident_range_cache = confident_shoot_range( get_weapon(), get_most_accurate_sight( get_weapon() ) ); } else { - confident_range_cache = weapon.reach_range( *this ); + confident_range_cache = get_weapon().reach_range( *this ); } } void npc::form_opinion( const player &u ) { // FEAR - if( u.weapon.is_gun() ) { + if( u.get_weapon().is_gun() ) { // TODO: Make bows not guns - if( weapon.is_gun() ) { + if( get_weapon().is_gun() ) { op_of_u.fear += 2; } else { op_of_u.fear += 6; } - } else if( u.weapon_value( u.weapon ) > 20 ) { + } else if( u.weapon_value( u.get_weapon() ) > 20 ) { op_of_u.fear += 2; } else if( !u.is_armed() ) { // Unarmed, but actually unarmed ("unarmed weapons" are not unarmed) @@ -1285,7 +1288,7 @@ void npc::form_opinion( const player &u ) op_of_u.trust += 1; } - if( u.weapon.is_gun() ) { + if( u.get_weapon().is_gun() ) { op_of_u.trust -= 2; } else if( !u.is_armed() ) { op_of_u.trust += 2; @@ -1489,14 +1492,14 @@ void npc::decide_needs() for( auto &elem : needrank ) { elem = 20; } - if( weapon.is_gun() ) { - int ups_drain = weapon.get_gun_ups_drain(); + if( get_weapon().is_gun() ) { + int ups_drain = get_weapon().get_gun_ups_drain(); if( ups_drain > 0 ) { int ups_charges = charges_of( itype_UPS_off, ups_drain ) + charges_of( itype_UPS_off, ups_drain ); needrank[need_ammo] = static_cast( ups_charges ) / ups_drain; } else { - needrank[need_ammo] = get_ammo( ammotype( *weapon.type->gun->ammo.begin() ) ).size(); + needrank[need_ammo] = get_ammo( ammotype( *get_weapon().type->gun->ammo.begin() ) ).size(); } needrank[need_ammo] *= 5; } @@ -1504,12 +1507,12 @@ void npc::decide_needs() needrank[need_safety] = 1; } - needrank[need_weapon] = weapon_value( weapon ); + needrank[need_weapon] = weapon_value( get_weapon() ); needrank[need_food] = 15.0f - ( max_stored_kcal() - get_stored_kcal() ) / 10.0f; needrank[need_drink] = 15 - get_thirst(); invslice slice = inv.slice(); for( auto &i : slice ) { - item inventory_item = i->front(); + item &inventory_item = *i->front(); if( const item *food = inventory_item.get_food() ) { needrank[ need_food ] += nutrition_for( *food ) / 4.0; needrank[ need_drink ] += food->get_comestible()->quench / 4.0; @@ -1682,15 +1685,16 @@ void npc::shop_restock() total_space = units::from_liter( 5000 ); } - std::list ret; + ItemList ret; int shop_value = 75000; if( my_fac ) { shop_value = my_fac->wealth * 0.0075; if( mission == NPC_MISSION_SHOPKEEP && !my_fac->currency.is_empty() ) { - item my_currency( my_fac->currency ); - if( !my_currency.is_null() ) { - my_currency.set_owner( *this ); - int my_amount = rng( 5, 15 ) * shop_value / 100 / my_currency.price( true ); + //TODO!: check + item *my_currency = item::spawn( my_fac->currency ); + if( !my_currency->is_null() ) { + my_currency->set_owner( *this ); + int my_amount = rng( 5, 15 ) * shop_value / 100 / my_currency->price( true ); for( int lcv = 0; lcv < my_amount; lcv++ ) { ret.push_back( my_currency ); } @@ -1701,12 +1705,12 @@ void npc::shop_restock() int count = 0; bool last_item = false; while( shop_value > 0 && total_space > 0_ml && !last_item ) { - item tmpit = item_group::item_from( from, calendar::turn ); - if( !tmpit.is_null() && total_space >= tmpit.volume() ) { - tmpit.set_owner( *this ); + item *tmpit = item_group::item_from( from, calendar::turn ); + if( !tmpit->is_null() && total_space >= tmpit->volume() ) { + tmpit->set_owner( *this ); ret.push_back( tmpit ); - shop_value -= tmpit.price( true ); - total_space -= tmpit.volume(); + shop_value -= tmpit->price( true ); + total_space -= tmpit->volume(); count += 1; last_item = count > 10 && one_in( 100 ); } @@ -1755,7 +1759,7 @@ int npc::value( const item &it, int market_price ) const int ret = 0; // TODO: Cache own weapon value (it can be a bit expensive to compute 50 times/turn) - double weapon_val = weapon_value( it ) - weapon_value( weapon ); + double weapon_val = weapon_value( it ) - weapon_value( get_weapon() ); if( weapon_val > 0 ) { ret += weapon_val; } @@ -1778,7 +1782,7 @@ int npc::value( const item &it, int market_price ) const } if( it.is_ammo() ) { - if( weapon.is_gun() && weapon.ammo_types().count( it.ammo_type() ) ) { + if( get_weapon().is_gun() && get_weapon().ammo_types().count( it.ammo_type() ) ) { // TODO: magazines - don't count ammo as usable if the weapon isn't. ret += 14; } @@ -2145,7 +2149,9 @@ void npc::npc_dismount() remove_effect( effect_riding ); if( mounted_creature->has_flag( MF_RIDEABLE_MECH ) && !mounted_creature->type->mech_weapon.is_empty() ) { - remove_item( weapon ); + item & it=get_weapon(); + it.detach(); + it.destroy(); } mounted_creature->remove_effect( effect_ridden ); mounted_creature->add_effect( effect_ai_waiting, 5_turns ); @@ -2158,7 +2164,7 @@ int npc::smash_ability() const { if( !is_hallucination() && ( !is_player_ally() || rules.has_flag( ally_rule::allow_bash ) ) ) { ///\EFFECT_STR_NPC increases smash ability - return str_cur + weapon.damage_melee( DT_BASH ); + return str_cur + get_weapon().damage_melee( DT_BASH ); } // Not allowed to bash @@ -2172,7 +2178,7 @@ float npc::danger_assessment() float npc::average_damage_dealt() { - return static_cast( melee_value( weapon ) ); + return static_cast( melee_value( get_weapon() ) ); } bool npc::bravery_check( int diff ) @@ -2249,7 +2255,7 @@ int npc::print_info( const catacurses::window &w, int line, int vLines, int colu } if( is_armed() ) { - trim_and_print( w, point( column, line++ ), iWidth, c_red, _( "Wielding a %s" ), weapon.tname() ); + trim_and_print( w, point( column, line++ ), iWidth, c_red, _( "Wielding a %s" ), get_weapon().tname() ); } const auto enumerate_print = [ w, last_line, column, iWidth, &line ]( const std::string & str_in, @@ -2260,8 +2266,9 @@ int npc::print_info( const catacurses::window &w, int line, int vLines, int colu } }; - const std::string worn_str = enumerate_as_string( worn.begin(), worn.end(), []( const item & it ) { - return it.tname(); + const std::string worn_str = enumerate_as_string( worn.begin(), + worn.end(), []( const item * const & it ) { + return it->tname(); } ); if( !worn_str.empty() ) { const std::string wearing = _( "Wearing: " ) + worn_str; @@ -2751,7 +2758,7 @@ float npc::speed_rating() const return ret; } -bool npc::dispose_item( item_location &&obj, const std::string & ) +bool npc::dispose_item( item &obj, const std::string & ) { using dispose_option = struct { int moves; @@ -2761,22 +2768,23 @@ bool npc::dispose_item( item_location &&obj, const std::string & ) std::vector opts; for( auto &e : worn ) { - if( e.can_holster( *obj ) ) { - auto ptr = dynamic_cast( e.type->get_use( "holster" )->get_actor_ptr() ); + if( e->can_holster( obj ) ) { + auto ptr = dynamic_cast( e->type->get_use( "holster" )->get_actor_ptr() ); opts.emplace_back( dispose_option { - item_store_cost( *obj, e, false, ptr->draw_cost ), - [this, ptr, &e, &obj]{ ptr->store( *this, e, *obj ); } + item_store_cost( obj, *e, false, ptr->draw_cost ), + [this, ptr, &e, &obj]{ ptr->store( *this, *e, obj ); } } ); } } - if( volume_carried() + obj->volume() <= volume_capacity() ) { + if( volume_carried() + obj.volume() <= volume_capacity() ) { opts.emplace_back( dispose_option { - item_handling_cost( *obj ), + item_handling_cost( obj ), [this, &obj] { - moves -= item_handling_cost( *obj ); - inv.add_item_keep_invlet( *obj ); - obj.remove_item(); + moves -= item_handling_cost( obj ); + inv.add_item_keep_invlet( obj ); + obj.detach(); + obj.destroy(); inv.unsort(); } } ); @@ -2784,8 +2792,8 @@ bool npc::dispose_item( item_location &&obj, const std::string & ) if( opts.empty() ) { // Drop it - g->m.add_item_or_charges( pos(), *obj ); - obj.remove_item(); + obj.detach(); + g->m.add_item_or_charges( pos(), obj ); return true; } diff --git a/src/npc.h b/src/npc.h index d2280e87e4a0..974468b0b70b 100644 --- a/src/npc.h +++ b/src/npc.h @@ -28,7 +28,6 @@ #include "int_id.h" #include "inventory.h" #include "item.h" -#include "item_location.h" #include "line.h" #include "lru_cache.h" #include "optional.h" @@ -935,9 +934,9 @@ class npc : public player void update_worst_item_value(); int value( const item &it ) const; int value( const item &it, int market_price ) const; - bool wear_if_wanted( const item &it, std::string &reason ); - void start_read( item_location loc, player *pl ); - void finish_read( item_location loc ); + bool wear_if_wanted( item &it, std::string &reason ); + void start_read( item &it, player *pl ); + void finish_read( item *it ); bool can_read( const item &book, std::vector &fail_reasons ); int time_to_read( const item &book, const player &reader ) const; void do_npc_read(); @@ -1103,10 +1102,10 @@ class npc : public player const item &find_reloadable() const; item &find_reloadable(); /** Finds ammo the NPC could use to reload a given object */ - item_location find_usable_ammo( const item &weap ); - item_location find_usable_ammo( const item &weap ) const; + item *find_usable_ammo( const item &weap ); + item *find_usable_ammo( const item &weap ) const; - bool dispose_item( item_location &&obj, const std::string &prompt = std::string() ) override; + bool dispose_item( item &obj, const std::string &prompt = std::string() ) override; void aim(); void do_reload( const item &it ); @@ -1157,8 +1156,8 @@ class npc : public player // Drop wgt and vol, including all items with less value than min_val void drop_items( units::mass drop_weight, units::volume drop_volume, int min_val = 0 ); /** Picks up items and returns a list of them. */ - std::list pick_up_item_map( const tripoint &where ); - std::list pick_up_item_vehicle( vehicle &veh, int part_index ); + ItemList pick_up_item_map( const tripoint &where ); + ItemList pick_up_item_vehicle( vehicle &veh, int part_index ); bool has_item_whitelist() const; bool item_name_whitelisted( const std::string &to_match ); @@ -1176,7 +1175,7 @@ class npc : public player bool alt_attack(); void heal_player( player &patient ); void heal_self(); - void pretend_heal( player &patient, item used ); // healing action of hallucinations + void pretend_heal( player &patient, item& used ); // healing action of hallucinations void mug_player( Character &mark ); void look_for_player( const Character &sought ); // Do we have an idea of where u are? @@ -1375,7 +1374,7 @@ class npc : public player private: // the weapon we're actually holding when using bionic fake guns - item real_weapon; + item *real_weapon; // the index of the bionics for the fake gun; int cbm_weapon_index = -1; diff --git a/src/npcmove.cpp b/src/npcmove.cpp index 71b0f0e28572..58263cb96142 100644 --- a/src/npcmove.cpp +++ b/src/npcmove.cpp @@ -329,7 +329,7 @@ std::vector npc::find_dangerous_explosives() const const explosion_iuse *actor = dynamic_cast( use->get_actor_ptr() ); const int safe_range = actor->explosion.safe_range(); - if( rl_dist( pos(), elem.position() ) >= safe_range ) { + if( rl_dist( pos(), elem->position() ) >= safe_range ) { continue; // Far enough. } @@ -339,7 +339,7 @@ std::vector npc::find_dangerous_explosives() const continue; // Consider only imminent dangers. } - result.emplace_back( elem.position(), safe_range ); + result.emplace_back( elem->position(), safe_range ); } return result; @@ -639,9 +639,9 @@ float npc::character_danger( const Character &uc ) const // TODO: Remove this when possible const player &u = dynamic_cast( uc ); float ret = 0.0; - bool u_gun = u.weapon.is_gun(); - bool my_gun = weapon.is_gun(); - double u_weap_val = u.weapon_value( u.weapon ); + bool u_gun = u.get_weapon().is_gun(); + bool my_gun = get_weapon().is_gun(); + double u_weap_val = u.weapon_value( u.get_weapon() ); const double &my_weap_val = ai_cache.my_weapon_value; if( u_gun && !my_gun ) { u_weap_val *= 1.5f; @@ -679,7 +679,7 @@ void npc::regen_ai_cache() ai_cache.can_heal.clear_all(); ai_cache.danger = 0.0f; ai_cache.total_danger = 0.0f; - ai_cache.my_weapon_value = weapon_value( weapon ); + ai_cache.my_weapon_value = weapon_value( get_weapon() ); ai_cache.dangerous_explosives = find_dangerous_explosives(); assess_danger(); @@ -734,8 +734,8 @@ void npc::move() const Creature *target = current_target(); const std::string &target_name = target != nullptr ? target->disp_name() : no_target_str; add_msg( m_debug, "NPC %s: target = %s, danger = %.1f, range = %d", - name, target_name, ai_cache.danger, weapon.is_gun() ? confident_shoot_range( weapon, - recoil_total() ) : weapon.reach_range( *this ) ); + name, target_name, ai_cache.danger, get_weapon().is_gun() ? confident_shoot_range( get_weapon(), + recoil_total() ) : get_weapon().reach_range( *this ) ); Character &player_character = get_player_character(); //faction opinion determines if it should consider you hostile @@ -982,7 +982,7 @@ void npc::execute_action( npc_action action ) worker_downtime(); break; case npc_reload: { - do_reload( weapon ); + do_reload( get_weapon() ); } break; @@ -1103,7 +1103,7 @@ void npc::execute_action( npc_action action ) break; case npc_shoot: { - auto mode = weapon.gun_current_mode(); + auto mode = get_weapon().gun_current_mode(); if( !mode ) { debugmsg( "NPC tried to shoot without valid mode" ); break; @@ -1349,7 +1349,7 @@ npc_action npc::method_of_attack() // except in emergency only fire bursts if danger > 0.5 and don't shoot at all at harmless targets std::vector> modes; if( rules.has_flag( ally_rule::use_guns ) || !is_player_ally() ) { - for( const auto &e : weapon.gun_all_modes() ) { + for( const auto &e : get_weapon().gun_all_modes() ) { modes.push_back( e ); } @@ -1390,7 +1390,7 @@ npc_action npc::method_of_attack() } // reach attacks are silent and consume no ammo so prefer these if available - int reach_range = weapon.reach_range( *this ); + int reach_range = get_weapon().reach_range( *this ); if( !trigdist ) { if( reach_range > 1 && reach_range >= dist && clear_shot_reach( pos(), tar ) ) { add_msg( m_debug, "%s is trying a reach attack", disp_name() ); @@ -1407,13 +1407,13 @@ npc_action npc::method_of_attack() // if the best mode is within the confident range try for a shot if( !modes.empty() && sees( *critter ) && has_los && confident_gun_mode_range( modes[ 0 ].second, cur_recoil ) >= dist ) { - if( cbm_weapon_index > 0 && !weapon.ammo_sufficient() && can_reload_current() ) { + if( cbm_weapon_index > 0 && !get_weapon().ammo_sufficient() && can_reload_current() ) { add_msg( m_debug, "%s is reloading", disp_name() ); return npc_reload; } - if( wont_hit_friend( tar, weapon, false ) ) { - weapon.gun_set_mode( modes[ 0 ].first ); + if( wont_hit_friend( tar, get_weapon(), false ) ) { + get_weapon().gun_set_mode( modes[ 0 ].first ); add_msg( m_debug, "%s is trying to shoot someone", disp_name() ); return npc_shoot; @@ -1438,15 +1438,15 @@ npc_action npc::method_of_attack() return npc_noop; } - if( !weapon.ammo_sufficient() && can_reload_current() ) { + if( !get_weapon().ammo_sufficient() && can_reload_current() ) { add_msg( m_debug, "%s is reloading", disp_name() ); return npc_reload; } } // TODO: Needs a check for transparent but non-passable tiles on the way - if( !modes.empty() && sees( *critter ) && aim_per_move( weapon, recoil ) > 0 && - confident_shoot_range( weapon, get_most_accurate_sight( weapon ) ) >= dist ) { + if( !modes.empty() && sees( *critter ) && aim_per_move( get_weapon(), recoil ) > 0 && + confident_shoot_range( get_weapon(), get_most_accurate_sight( get_weapon() ) ) >= dist ) { add_msg( m_debug, "%s is aiming", disp_name() ); if( critter->is_player() && player_character.sees( *this ) ) { add_msg( m_bad, _( "%s takes aim at you!" ), disp_name() ); @@ -1524,28 +1524,28 @@ const item &npc::find_reloadable() const bool npc::can_reload_current() { - if( !weapon.is_gun() || !wants_to_reload( *this, weapon ) ) { + if( !get_weapon().is_gun() || !wants_to_reload( *this, get_weapon() ) ) { return false; } - return static_cast( find_usable_ammo( weapon ) ); + return static_cast( find_usable_ammo( get_weapon() ) ); } -item_location npc::find_usable_ammo( const item &weap ) +item* npc::find_usable_ammo( const item &weap ) { if( !can_reload( weap ) ) { - return item_location(); + return nullptr; } auto loc = select_ammo( weap ).ammo; if( !loc || !wants_to_reload_with( weap, *loc ) ) { - return item_location(); + return nullptr; } return loc; } -item_location npc::find_usable_ammo( const item &weap ) const +item* npc::find_usable_ammo( const item &weap ) const { return const_cast( this )->find_usable_ammo( weap ); } @@ -1660,8 +1660,8 @@ bool npc::consume_cbm_items( const std::function &filter ) invslice slice = inv.slice(); int index = -1; for( size_t i = 0; i < slice.size(); i++ ) { - const item &it = slice[i]->front(); - const item &real_item = it.is_container() ? it.contents.front() : it; + item *&it = slice[i]->front(); + const item &real_item = it->is_container() ? it->contents.front() : *it; if( filter( real_item ) ) { index = i; break; @@ -1671,8 +1671,8 @@ bool npc::consume_cbm_items( const std::function &filter ) return false; } int old_moves = moves; - item_location loc = item_location( *this, &i_at( index ) ); - return consume( loc ) && old_moves != moves; + item* loc = &i_at( index ); + return consume( *loc ) && old_moves != moves; } bool npc::recharge_cbm() @@ -2128,7 +2128,8 @@ bool npc::wont_hit_friend( const tripoint &tar, const item &it, bool throwing ) bool npc::enough_time_to_reload( const item &gun ) const { - int rltime = item_reload_cost( gun, item( gun.ammo_default() ), + //TODO!: check this temp + int rltime = item_reload_cost( gun, *item::spawn_temporary( gun.ammo_default() ), gun.ammo_capacity() ); const float turns_til_reloaded = static_cast( rltime ) / get_speed(); @@ -2144,8 +2145,8 @@ bool npc::enough_time_to_reload( const item &gun ) const if( target->is_player() || target->is_npc() ) { auto &c = dynamic_cast( *target ); // TODO: Allow reloading if the player has a low accuracy gun - if( sees( c ) && c.weapon.is_gun() && rltime > 200 && - c.weapon.gun_range( true ) > distance + turns_til_reloaded / target_speed ) { + if( sees( c ) && c.get_weapon().is_gun() && rltime > 200 && + c.get_weapon().gun_range( true ) > distance + turns_til_reloaded / target_speed ) { // Don't take longer than 2 turns if player has a gun return false; } @@ -2157,12 +2158,12 @@ bool npc::enough_time_to_reload( const item &gun ) const void npc::aim() { - double aim_amount = aim_per_move( weapon, recoil ); + double aim_amount = aim_per_move( get_weapon(), recoil ); while( aim_amount > 0 && recoil > 0 && moves > 0 ) { moves--; recoil -= aim_amount; recoil = std::max( 0.0, recoil ); - aim_amount = aim_per_move( weapon, recoil ); + aim_amount = aim_per_move( get_weapon(), recoil ); } } @@ -2399,7 +2400,7 @@ void npc::move_to( const tripoint &pt, bool no_bashing, std::set *nomo } } else if( !no_bashing && smash_ability() > 0 && here.is_bashable( p ) && here.bash_rating( smash_ability(), p ) > 0 ) { - moves -= !is_armed() ? 80 : weapon.attack_cost() * 0.8; + moves -= !is_armed() ? 80 : get_weapon().attack_cost() * 0.8; here.bash( p, smash_ability() ); } else { if( attitude == NPCATT_MUG || @@ -2681,7 +2682,7 @@ void npc::move_pause() } // NPCs currently always aim when using a gun, even with no target // This simulates them aiming at stuff just at the edge of their range - if( !weapon.is_gun() ) { + if( !get_weapon().is_gun() ) { pause(); return; } @@ -2777,8 +2778,8 @@ void npc::see_item_say_smth( const itype_id &object, const std::string &smth ) map &here = get_map(); for( const tripoint &p : closest_points_first( pos(), 6 ) ) { if( here.sees_some_items( p, *this ) && sees( p ) ) { - for( const item &it : here.i_at( p ) ) { - if( one_in( 100 ) && ( it.typeId() == object ) ) { + for( const item * const &it : here.i_at( p ) ) { + if( one_in( 100 ) && ( it->typeId() == object ) ) { say( smth ); } } @@ -2910,8 +2911,8 @@ void npc::find_item() bool can_see = false; if( here.sees_some_items( p, *this ) && sees( p ) ) { can_see = true; - for( const item &it : m_stack ) { - consider_item( it, p ); + for( const item * const &it : m_stack ) { + consider_item( *it, p ); } } @@ -2939,8 +2940,8 @@ void npc::find_item() continue; } - for( const item &it : cargo->vehicle().get_items( cargo->part_index() ) ) { - consider_item( it, p ); + for( const item * const &it : cargo->vehicle().get_items( cargo->part_index() ) ) { + consider_item( *it, p ); } cache_tile(); } @@ -3047,10 +3048,10 @@ void npc::pick_up_item() bool u_see = player_character.sees( *this ) || player_character.sees( wanted_item_pos ); if( u_see ) { if( picked_up.size() == 1 ) { - add_msg( _( "%1$s picks up a %2$s." ), name, picked_up.front().tname() ); + add_msg( _( "%1$s picks up a %2$s." ), name, picked_up.front()->tname() ); } else if( picked_up.size() == 2 ) { - add_msg( _( "%1$s picks up a %2$s and a %3$s." ), name, picked_up.front().tname(), - picked_up.back().tname() ); + add_msg( _( "%1$s picks up a %2$s and a %3$s." ), name, picked_up.front()->tname(), + picked_up.back()->tname() ); } else if( picked_up.size() > 2 ) { add_msg( _( "%s picks up several items." ), name ); } else { @@ -3059,11 +3060,11 @@ void npc::pick_up_item() } for( auto &it : picked_up ) { - int itval = value( it ); + int itval = value( *it ); if( itval < worst_item_value ) { worst_item_value = itval; } - i_add( it ); + i_add( *it ); } moves -= 100; @@ -3072,16 +3073,16 @@ void npc::pick_up_item() } template -std::list npc_pickup_from_stack( npc &who, T &items ) +std::vector npc_pickup_from_stack( npc &who, T &items ) { const bool whitelisting = who.has_item_whitelist(); auto volume_allowed = who.volume_capacity() - who.volume_carried(); auto weight_allowed = who.weight_capacity() - who.weight_carried(); auto min_value = whitelisting ? 0 : who.minimum_item_value(); - std::list picked_up; + std::vector picked_up; for( auto iter = items.begin(); iter != items.end(); ) { - const item &it = *iter; + item &it = **iter; if( it.made_of( LIQUID ) ) { iter++; continue; @@ -3112,20 +3113,20 @@ std::list npc_pickup_from_stack( npc &who, T &items ) volume_allowed -= volume; weight_allowed -= weight; - picked_up.push_back( it ); + picked_up.push_back( &it ); iter = items.erase( iter ); } return picked_up; } -std::list npc::pick_up_item_map( const tripoint &where ) +ItemList npc::pick_up_item_map( const tripoint &where ) { map_stack stack = get_map().i_at( where ); return npc_pickup_from_stack( *this, stack ); } -std::list npc::pick_up_item_vehicle( vehicle &veh, int part_index ) +ItemList npc::pick_up_item_vehicle( vehicle &veh, int part_index ) { auto stack = veh.get_items( part_index ); return npc_pickup_from_stack( *this, stack ); @@ -3157,7 +3158,7 @@ void npc::drop_items( units::mass drop_weight, units::volume drop_volume, int mi // First fill our ratio vectors, so we know which things to drop first invslice slice = inv.slice(); for( size_t i = 0; i < slice.size(); i++ ) { - item &it = slice[i]->front(); + item &it = *slice[i]->front(); double wgt_ratio = 0.0; double vol_ratio = 0.0; if( value( it ) == 0 || value( it ) <= min_val ) { @@ -3225,9 +3226,9 @@ void npc::drop_items( units::mass drop_weight, units::volume drop_volume, int mi } } } - weight_dropped += slice[index]->front().weight(); - volume_dropped += slice[index]->front().volume(); - item dropped = i_rem( index ); + weight_dropped += slice[index]->front()->weight(); + volume_dropped += slice[index]->front()->volume(); + item &dropped = i_rem( index ); num_items_dropped++; if( num_items_dropped == 1 ) { item_name += dropped.tname(); @@ -3270,18 +3271,18 @@ bool npc::find_corpse_to_pulp() const map_stack items = here.i_at( p ); const item *found = nullptr; - for( const item &it : items ) + for( const item *const &it : items ) { // Pulp only stuff that revives, but don't pulp acid stuff // That is, if you aren't protected from this stuff! - if( it.can_revive() ) { + if( it->can_revive() ) { // If the first encountered corpse bleeds something dangerous then // it is not safe to bash. - if( is_dangerous_field( field_entry( it.get_mtype()->bloodType(), 1, 0_turns ) ) ) { + if( is_dangerous_field( field_entry( it->get_mtype()->bloodType(), 1, 0_turns ) ) ) { return nullptr; } - found = ⁢ + found = it; break; } } @@ -3309,12 +3310,12 @@ bool npc::find_corpse_to_pulp() if( corpse == nullptr ) { // If we're following the player, don't wander off to pulp corpses const tripoint &around = is_walking_with() ? player_character.pos() : pos(); - for( const item_location &location : here.get_active_items_in_radius( around, range, + for( item*&location : here.get_active_items_in_radius( around, range, special_item_type::corpse ) ) { - corpse = check_tile( location.position() ); + corpse = check_tile( location->position() ); if( corpse != nullptr ) { - pulp_location.emplace( location.position() ); + pulp_location.emplace( location->position() ); break; } @@ -3408,7 +3409,7 @@ bool npc::wield_better_weapon() invslice slice = inv.slice(); // Check if there's something better to wield - item *best = &weapon; + item *best = &get_weapon(); double best_value = -100.0; const int ups_charges = charges_of( itype_UPS ); @@ -3435,7 +3436,7 @@ bool npc::wield_better_weapon() } }; - compare_weapon( weapon ); + compare_weapon( get_weapon() ); // To prevent changing to barely better stuff best_value *= std::max( 1.0f, ai_cache.danger_assessment / 10.0f ); @@ -3446,7 +3447,7 @@ bool npc::wield_better_weapon() // Only compare melee weapons, guns, or holstered items if( node->is_melee() || node->is_gun() ) { compare_weapon( *node ); - } else if( node->get_use( "holster" ) && !node->contents.empty() && node != &weapon ) { + } else if( node->get_use( "holster" ) && !node->contents.empty() && node != &get_weapon() ) { // TODO: special case for "wield from wielded holster" const item &holstered = node->get_contained(); if( holstered.is_melee() || holstered.is_gun() ) { @@ -3460,7 +3461,7 @@ bool npc::wield_better_weapon() // Needs to check reload speed, RELOAD_ONE etc. // Until then, the NPCs should reload the guns as a last resort - if( best == &weapon ) { + if( best == &get_weapon() ) { add_msg( m_debug, "Wielded %s is best at %.1f, not switching", best->type->get_id().str(), best_value ); return false; @@ -3557,10 +3558,10 @@ bool npc::alt_attack() used_dangerous = used_dangerous || dangerous; }; - check_alt_item( weapon ); + check_alt_item( get_weapon() ); for( auto &sl : inv.slice() ) { // TODO: Cached values - an itype slot maybe? - check_alt_item( sl->front() ); + check_alt_item( *sl->front() ); } if( used == nullptr ) { @@ -3693,7 +3694,7 @@ void npc::heal_player( player &patient ) } -void npc:: pretend_heal( player &patient, item used ) +void npc:: pretend_heal( player &patient, item& used ) { if( get_player_character().sees( *this ) ) { add_msg( _( "%1$s heals %2$s." ), disp_name(), @@ -3706,19 +3707,19 @@ void npc:: pretend_heal( player &patient, item used ) void npc::heal_self() { if( has_effect( effect_asthma ) ) { - item &treatment = null_item_reference(); + item *treatment = &null_item_reference(); std::string iusage = "OXYGEN_BOTTLE"; if( has_charges( itype_inhaler, 1 ) ) { - treatment = inv.find_item( inv.position_by_type( itype_inhaler ) ); + treatment = &inv.find_item( inv.position_by_type( itype_inhaler ) ); iusage = "INHALER"; } else if( has_charges( itype_oxygen_tank, 1 ) ) { - treatment = inv.find_item( inv.position_by_type( itype_oxygen_tank ) ); + treatment = &inv.find_item( inv.position_by_type( itype_oxygen_tank ) ); } else if( has_charges( itype_smoxygen_tank, 1 ) ) { - treatment = inv.find_item( inv.position_by_type( itype_smoxygen_tank ) ); + treatment = &inv.find_item( inv.position_by_type( itype_smoxygen_tank ) ); } - if( !treatment.is_null() ) { - treatment.type->invoke( *this, treatment, pos(), iusage ); - consume_charges( treatment, 1 ); + if( !treatment->is_null() ) { + treatment->type->invoke( *this, *treatment, pos(), iusage ); + consume_charges( *treatment, 1 ); return; } } @@ -3752,8 +3753,7 @@ void npc::use_painkiller() if( get_player_character().sees( *this ) ) { add_msg( _( "%1$s takes some %2$s." ), disp_name(), it->tname() ); } - item_location loc = item_location( *this, it ); - consume( loc ); + consume( *it ); moves = 0; } } @@ -3890,7 +3890,7 @@ bool npc::consume_food() int want_quench = std::max( 0, get_thirst() ); invslice slice = inv.slice(); for( size_t i = 0; i < slice.size(); i++ ) { - const item &it = slice[i]->front(); + const item &it = *slice[i]->front(); if( const item *food_item = it.get_food() ) { float cur_weight = rate_food( *food_item, want_hunger, want_quench ); // Note: will_eat is expensive, avoid calling it if possible @@ -3913,8 +3913,8 @@ bool npc::consume_food() // consume doesn't return a meaningful answer, we need to compare moves // TODO: Make player::consume return false if it fails to consume int old_moves = moves; - item_location loc = item_location( *this, &i_at( index ) ); - bool consumed = consume( loc ) && old_moves != moves; + item* loc = &i_at( index ); + bool consumed = consume( *loc ) && old_moves != moves; if( !consumed ) { debugmsg( "%s failed to consume %s", name, i_at( index ).tname() ); } @@ -3966,8 +3966,8 @@ void npc::mug_player( Character &mark ) double best_value = minimum_item_value() * value_mod; item *to_steal = nullptr; invslice slice = mark.inv.slice(); - for( std::list *stack : slice ) { - item &front_stack = stack->front(); + for( ItemList *stack : slice ) { + item &front_stack = *stack->front(); if( value( front_stack ) >= best_value && can_pick_volume( front_stack ) && can_pick_weight( front_stack, true ) ) { @@ -3983,18 +3983,18 @@ void npc::mug_player( Character &mark ) moves -= 100; return; } - item stolen; + item *stolen=nullptr; if( !is_hallucination() ) { - stolen = mark.i_rem( to_steal ); - i_add( stolen ); - } - if( mark.is_npc() ) { - if( u_see ) { - add_msg( _( "%1$s takes %2$s's %3$s." ), name, mark.name, stolen.tname() ); - } - } else { - add_msg( m_bad, _( "%1$s takes your %2$s." ), name, stolen.tname() ); - } + stolen = &mark.i_rem( to_steal ); + i_add( *stolen ); + if( mark.is_npc() ) { + if( u_see ) { + add_msg( _( "%1$s takes %2$s's %3$s." ), name, mark.name, stolen->tname() ); + } + } else { + add_msg( m_bad, _( "%1$s takes your %2$s." ), name, stolen->tname() ); + } + } moves -= 100; if( !mark.is_npc() ) { op_of_u.value -= rng( 0, 1 ); // Decrease the value of the player @@ -4578,14 +4578,14 @@ void npc::do_reload( const item &it ) // Note: we may be reloading the magazine inside, not the gun itself // Maybe TODO: allow reload functions to understand such reloads instead of const casts item &target = const_cast( *reload_opt.target ); - item_location &usable_ammo = reload_opt.ammo; + item *usable_ammo = reload_opt.ammo; int qty = std::max( 1, std::min( usable_ammo->charges, it.ammo_capacity() - it.ammo_remaining() ) ); int reload_time = item_reload_cost( it, *usable_ammo, qty ); // TODO: Consider printing this info to player too const std::string ammo_name = usable_ammo->tname(); - if( !target.reload( *this, std::move( usable_ammo ), qty ) ) { + if( !target.reload( *this, *usable_ammo, qty ) ) { debugmsg( "do_reload failed: item %s could not be reloaded with %ld charge(s) of %s", it.tname(), qty, ammo_name ); return; @@ -4628,14 +4628,14 @@ bool npc::adjust_worn() }; for( auto &elem : worn ) { - if( !elem.has_flag( "SPLINT" ) ) { + if( !elem->has_flag( "SPLINT" ) ) { continue; } - if( !covers_broken( elem, elem.get_side() ) ) { - const bool needs_change = covers_broken( elem, opposite_side( elem.get_side() ) ); + if( !covers_broken( *elem, elem->get_side() ) ) { + const bool needs_change = covers_broken( *elem, opposite_side( elem->get_side() ) ); // Try to change side (if it makes sense), or take off. - if( ( needs_change && change_side( elem ) ) || takeoff( elem ) ) { + if( ( needs_change && change_side( *elem ) ) || takeoff( *elem ) ) { return true; } } diff --git a/src/npctalk.cpp b/src/npctalk.cpp index c83d25fb0e67..ad7952ce12cb 100644 --- a/src/npctalk.cpp +++ b/src/npctalk.cpp @@ -1594,18 +1594,18 @@ void parse_tags( std::string &phrase, const Character &u, const Character &me, // Special, dynamic tags go here if( tag == "" ) { - phrase.replace( fa, l, remove_color_tags( u.weapon.tname() ) ); + phrase.replace( fa, l, remove_color_tags( u.get_weapon().tname() ) ); } else if( tag == "" ) { if( !me.is_armed() ) { phrase.replace( fa, l, _( "fists" ) ); } else { - phrase.replace( fa, l, remove_color_tags( me.weapon.tname() ) ); + phrase.replace( fa, l, remove_color_tags( me.get_weapon().tname() ) ); } } else if( tag == "" ) { - if( !me.weapon.is_gun() ) { + if( !me.get_weapon().is_gun() ) { phrase.replace( fa, l, _( "BADAMMO" ) ); } else { - phrase.replace( fa, l, me.weapon.ammo_current()->nname( 1 ) ); + phrase.replace( fa, l, me.get_weapon().ammo_current()->nname( 1 ) ); } } else if( tag == "" ) { std::string activity_name; @@ -1634,16 +1634,17 @@ void parse_tags( std::string &phrase, const Character &u, const Character &me, } else if( tag == "" ) { phrase.replace( fa, l, item::nname( item_type, 2 ) ); } else if( tag == "" ) { - item tmp( item_type ); - phrase.replace( fa, l, format_money( tmp.price( true ) ) ); + //TODO!: check these temps + item *tmp=item::spawn_temporary( item_type ); + phrase.replace( fa, l, format_money( tmp->price( true ) ) ); } else if( tag == "" ) { - item tmp( item_type ); - tmp.charges = me.charges_of( item_type ); - phrase.replace( fa, l, format_money( tmp.price( true ) ) ); + item *tmp=item::spawn_temporary( item_type ); + tmp->charges = me.charges_of( item_type ); + phrase.replace( fa, l, format_money( tmp->price( true ) ) ); } else if( tag == "" ) { - item tmp( item_type ); - tmp.charges = u.charges_of( item_type ); - phrase.replace( fa, l, format_money( tmp.price( true ) ) ); + item *tmp=item::spawn_temporary( item_type ); + tmp->charges = u.charges_of( item_type ); + phrase.replace( fa, l, format_money( tmp->price( true ) ) ); } else if( !tag.empty() ) { debugmsg( "Bad tag. '%s' (%d - %d)", tag.c_str(), fa, fb ); phrase.replace( fa, fb - fa + 1, "????" ); @@ -2059,28 +2060,30 @@ void talk_effect_fun_t::set_u_buy_item( const itype_id &item_name, int cost, int return; } if( container_name.empty() ) { - item new_item = item( item_name, calendar::turn ); - if( new_item.count_by_charges() ) { - new_item.mod_charges( count - 1 ); - u.i_add( new_item ); + item *new_item = item::spawn( item_name, calendar::turn ); + if( new_item->count_by_charges() ) { + new_item->mod_charges( count - 1 ); + u.i_add( *new_item ); } else { for( int i_cnt = 0; i_cnt < count; i_cnt++ ) { - u.i_add( new_item ); + u.i_add( *item::spawn(*new_item) ); } + new_item->destroy(); } if( count == 1 ) { //~ %1%s is the NPC name, %2$s is an item - popup( _( "%1$s gives you a %2$s." ), p.name, new_item.tname() ); + popup( _( "%1$s gives you a %2$s." ), p.name, new_item->tname() ); } else { //~ %1%s is the NPC name, %2$d is a number of items, %3$s are items - popup( _( "%1$s gives you %2$d %3$s." ), p.name, count, new_item.tname() ); + popup( _( "%1$s gives you %2$d %3$s." ), p.name, count, new_item->tname() ); } } else { - item container( container_name, calendar::turn ); - container.put_in( item( item_name, calendar::turn, count ) ); - u.i_add( container ); + //TODO!: check + item *container = item::spawn( container_name, calendar::turn ); + container->put_in( *item::spawn( item_name, calendar::turn, count ) ); + u.i_add( *container ); //~ %1%s is the NPC name, %2$s is an item - popup( _( "%1$s gives you a %2$s." ), p.name, container.tname() ); + popup( _( "%1$s gives you a %2$s." ), p.name, container->tname() ); } }; @@ -2096,12 +2099,12 @@ void talk_effect_fun_t::set_u_sell_item( const itype_id &item_name, int cost, in npc &p = *d.beta; player &u = *d.alpha; if( item::count_by_charges( item_name ) && u.has_charges( item_name, count ) ) { - for( const item &it : u.use_charges( item_name, count ) ) { - p.i_add( it ); + for( item *&it : u.use_charges( item_name, count ) ) { + p.i_add( *it ); } } else if( u.has_amount( item_name, count ) ) { - for( const item &it : u.use_amount( item_name, count ) ) { - p.i_add( it ); + for( item *&it : u.use_amount( item_name, count ) ) { + p.i_add( *it ); } } else { //~ %1$s is a translated item name @@ -2127,15 +2130,17 @@ void talk_effect_fun_t::set_consume_item( const JsonObject &jo, const std::strin jo.read( member, item_name, true ); function = [is_npc, item_name, count]( const dialogue & d ) { // this is stupid, but I couldn't get the assignment to work + //TODO!: You can say that again brother const auto consume_item = [&]( player & p, const itype_id & item_name, int count ) { - item old_item( item_name ); if( p.has_charges( item_name, count ) ) { p.use_charges( item_name, count ); } else if( p.has_amount( item_name, count ) ) { p.use_amount( item_name, count ); } else { //~ %1%s is the "You" or the NPC name, %2$s are a translated item name - popup( _( "%1$s doesn't have a %2$s!" ), p.disp_name(), old_item.tname() ); + //TODO!: Push this up? Hard but valuable + item * old_item=item::spawn_temporary(item_name); + popup( _( "%1$s doesn't have a %2$s!" ), p.disp_name(), old_item->tname() ); } }; if( is_npc ) { @@ -2322,15 +2327,16 @@ void talk_effect_fun_t::set_bulk_trade_accept( bool is_trade, bool is_npc ) buyer = d.alpha; } int seller_has = seller->charges_of( d.cur_item ); - item tmp( d.cur_item ); - tmp.charges = seller_has; + //TODO!: check this, I don't think we should be spawning here, just moving + item *tmp=item::spawn( d.cur_item ); + tmp->charges = seller_has; if( is_trade ) { - int price = tmp.price( true ) * ( is_npc ? -1 : 1 ) + d.beta->op_of_u.owed; + int price = tmp->price( true ) * ( is_npc ? -1 : 1 ) + d.beta->op_of_u.owed; if( d.beta->get_faction() && !d.beta->get_faction()->currency.is_empty() ) { const itype_id &pay_in = d.beta->get_faction()->currency; - item pay( pay_in ); - if( d.beta->value( pay ) > 0 ) { - int required = price / d.beta->value( pay ); + item *pay=item::spawn_temporary( pay_in ); + if( d.beta->value( *pay ) > 0 ) { + int required = price / d.beta->value( *pay ); int buyer_has = required; if( is_npc ) { buyer_has = std::min( buyer_has, buyer->charges_of( pay_in ) ); @@ -2339,23 +2345,23 @@ void talk_effect_fun_t::set_bulk_trade_accept( bool is_trade, bool is_npc ) if( buyer_has == 1 ) { //~ %1%s is the NPC name, %2$s is an item popup( _( "%1$s gives you a %2$s." ), d.beta->disp_name(), - pay.tname() ); + pay->tname() ); } else if( buyer_has > 1 ) { //~ %1%s is the NPC name, %2$d is a number of items, %3$s are items popup( _( "%1$s gives you %2$d %3$s." ), d.beta->disp_name(), buyer_has, - pay.tname() ); + pay->tname() ); } } for( int i = 0; i < buyer_has; i++ ) { - seller->i_add( pay ); - price -= d.beta->value( pay ); + seller->i_add( *item::spawn(*pay) ); + price -= d.beta->value( *pay ); } } d.beta->op_of_u.owed = price; } } seller->use_charges( d.cur_item, seller_has ); - buyer->i_add( tmp ); + buyer->i_add( *tmp ); }; } @@ -3337,14 +3343,14 @@ std::string give_item_to( npc &p, bool allow_use ) return _( "No thanks, I'm good." ); } avatar &you = get_avatar(); - item_location loc = game_menus::inv::titled_menu( you, _( "Offer what?" ), + item* loc = game_menus::inv::titled_menu( you, _( "Offer what?" ), _( "You have no items to offer." ) ); if( !loc ) { return _( "Changed your mind?" ); } item &given = *loc; - if( ( &given == &you.weapon && given.has_flag( "NO_UNWIELD" ) ) || ( you.is_worn( given ) && + if( ( &given == &you.get_weapon() && given.has_flag( "NO_UNWIELD" ) ) || ( you.is_worn( given ) && given.has_flag( "NO_TAKEOFF" ) ) ) { // Bionic weapon or shackles return _( "How?" ); @@ -3356,12 +3362,12 @@ std::string give_item_to( npc &p, bool allow_use ) bool taken = false; std::string reason = _( "Nope." ); - int our_ammo = p.ammo_count_for( p.weapon ); + int our_ammo = p.ammo_count_for( p.get_weapon() ); int new_ammo = p.ammo_count_for( given ); const double new_weapon_value = p.weapon_value( given, new_ammo ); - const double cur_weapon_value = p.weapon_value( p.weapon, our_ammo ); + const double cur_weapon_value = p.weapon_value( p.get_weapon(), our_ammo ); add_msg( m_debug, "NPC evaluates own %s (%d ammo): %0.1f", - p.weapon.typeId().str(), our_ammo, cur_weapon_value ); + p.get_weapon().typeId().str(), our_ammo, cur_weapon_value ); add_msg( m_debug, "NPC evaluates your %s (%d ammo): %0.1f", given.typeId().str(), new_ammo, new_weapon_value ); if( allow_use ) { @@ -3370,6 +3376,7 @@ std::string give_item_to( npc &p, bool allow_use ) if( consume_res != REFUSED ) { if( consume_res == CONSUMED_ALL ) { you.i_rem( &given ); + given.destroy(); } you.moves -= 100; if( given.is_container() ) { diff --git a/src/npctalk_funcs.cpp b/src/npctalk_funcs.cpp index 2b5d48ba89d7..24822297e413 100644 --- a/src/npctalk_funcs.cpp +++ b/src/npctalk_funcs.cpp @@ -451,13 +451,13 @@ void talk_function::insult_combat( npc &p ) void talk_function::bionic_install( npc &p ) { - item_location bionic = game_menus::inv::install_bionic( g->u, g->u, true ); + item* bionic = game_menus::inv::install_bionic( g->u, g->u, true ); if( !bionic ) { return; } - const item *tmp = bionic.get_item(); + const item *tmp = bionic; const itype &it = *tmp->type; signed int price = tmp->price( true ) * 2; @@ -467,7 +467,8 @@ void talk_function::bionic_install( npc &p ) //Makes the doctor awesome at installing but not perfect if( g->u.can_install_bionics( it, p, false, 20 ) ) { - bionic.remove_item(); + bionic->detach(); + bionic->destroy(); g->u.install_bionics( it, p, false, 20 ); } } @@ -479,7 +480,7 @@ void talk_function::bionic_remove( npc &p ) popup( _( "You don't have any bionics installed…" ) ); return; } - + //TODO!: check all the temps in here std::vector bionic_types; std::vector bionic_names; for( const bionic &bio : all_bio ) { @@ -489,8 +490,8 @@ void talk_function::bionic_remove( npc &p ) bio.id != bio_power_storage_mkII ) { bionic_types.push_back( bio.info().itype() ); if( bio.info().itype().is_valid() ) { - item tmp = item( bio.id.str(), calendar::start_of_cataclysm ); - bionic_names.push_back( tmp.tname() + " - " + format_money( 50000 + ( tmp.price( true ) / 4 ) ) ); + item *tmp = item::spawn_temporary( bio.id.str(), calendar::start_of_cataclysm ); + bionic_names.push_back( tmp->tname() + " - " + format_money( 50000 + ( tmp->price( true ) / 4 ) ) ); } else { bionic_names.push_back( bio.id.str() + " - " + format_money( 50000 ) ); } @@ -508,7 +509,7 @@ void talk_function::bionic_remove( npc &p ) int price; if( bionic_types[bionic_index].is_valid() ) { - int tmp = item( bionic_types[bionic_index], calendar::start_of_cataclysm ).price( true ); + int tmp = item::spawn_temporary( bionic_types[bionic_index], calendar::start_of_cataclysm )->price( true ); price = 50000 + ( tmp / 4 ); } else { price = 50000; @@ -545,8 +546,8 @@ void talk_function::give_equipment( npc &p ) debugmsg( "Chosen index is outside of available item range!" ); chosen = 0; } - item it = *giving[chosen].loc.get_item(); - giving[chosen].loc.remove_item(); + item &it = *giving[chosen].loc; + giving[chosen].loc->detach(); popup( _( "%1$s gives you a %2$s" ), p.name, it.tname() ); it.set_owner( g->u ); g->u.i_add( it ); @@ -833,7 +834,7 @@ void talk_function::drop_stolen_item( npc &p ) map &here = get_map(); for( auto &elem : g->u.inv_dump() ) { if( elem->is_old_owner( p ) ) { - item to_drop = g->u.i_rem( elem ); + item &to_drop = g->u.i_rem( elem ); to_drop.remove_old_owner(); to_drop.set_owner( p ); here.add_item_or_charges( g->u.pos(), to_drop ); diff --git a/src/npctrade.cpp b/src/npctrade.cpp index bee486cfcca0..3830ca47ea62 100644 --- a/src/npctrade.cpp +++ b/src/npctrade.cpp @@ -42,20 +42,21 @@ static const skill_id skill_barter( "barter" ); void npc_trading::transfer_items( std::vector &stuff, player &giver, - player &receiver, std::list &from_map, + player &receiver, std::list &from_map, bool npc_gives ) { + //TODO!: check this, it probably needs redoing wrt location for( item_pricing &ip : stuff ) { if( !ip.selected ) { continue; } - if( ip.loc.get_item() == nullptr ) { + if( ip.loc == nullptr ) { debugmsg( "Null item being traded in npc_trading::transfer_items" ); continue; } - item gift = *ip.loc.get_item(); + item &gift = *ip.loc; gift.set_owner( receiver ); int charges = npc_gives ? ip.u_charges : ip.npc_charges; int count = npc_gives ? ip.u_has : ip.npc_has; @@ -69,7 +70,7 @@ void npc_trading::transfer_items( std::vector &stuff, player &give } } - if( ip.loc.where() == item_location::type::character ) { + if( ip.loc->where() == item::item_location_type::character ) { const auto filter = [&]( const item & item ) //To make sure that we are removing proper item ->bool { return item.stacks_with( gift, true ); @@ -84,11 +85,11 @@ void npc_trading::transfer_items( std::vector &stuff, player &give } } else { if( ip.charges > 0 ) { - ip.loc.get_item()->set_var( "trade_charges", charges ); + ip.loc->set_var( "trade_charges", charges ); } else { - ip.loc.get_item()->set_var( "trade_amount", 1 ); + ip.loc->set_var( "trade_amount", 1 ); } - from_map.push_back( &ip.loc ); + from_map.push_back( ip.loc ); } } } @@ -98,21 +99,21 @@ std::vector npc_trading::init_selling( npc &np ) std::vector result; invslice slice = np.inv.slice(); for( auto &i : slice ) { - item &it = i->front(); + item &it = *i->front(); const int price = it.price( true ); int val = np.value( it ); if( np.wants_to_sell( it, val, price ) ) { - result.emplace_back( np, i->front(), val, static_cast( i->size() ) ); + result.emplace_back( *i->front(), val, static_cast( i->size() ) ); } } if( np.will_exchange_items_freely() && - !np.weapon.is_null() && - !np.weapon.has_flag( "NO_UNWIELD" ) + !np.get_weapon().is_null() && + !np.get_weapon().has_flag( "NO_UNWIELD" ) ) { - result.emplace_back( np, np.weapon, np.value( np.weapon ), false ); + result.emplace_back( np.get_weapon(), np.value( np.get_weapon() ), false ); } return result; @@ -138,8 +139,8 @@ double npc_trading::net_price_adjustment( const player &buyer, const player &sel template void buy_helper( T &src, Callback cb ) { - src.visit_items( [&src, &cb]( item * node ) { - cb( std::move( item_location( src, node ) ), 1 ); + src.visit_items( [&cb]( item * node ) { + cb( node, 1 ); return VisitResponse::SKIP; } ); @@ -157,9 +158,8 @@ std::vector npc_trading::init_buying( player &buyer, player &selle double adjust = net_price_adjustment( buyer, seller ); - const auto check_item = [fac, adjust, is_npc, &np, &result, &seller]( item_location && - loc, int count = 1 ) { - item *it_ptr = loc.get_item(); + const auto check_item = [fac, adjust, is_npc, &np, &result, &seller]( item* loc, int count = 1 ) { + item *it_ptr = loc; if( it_ptr == nullptr || it_ptr->is_null() ) { return; } @@ -174,18 +174,18 @@ std::vector npc_trading::init_buying( player &buyer, player &selle int val = np.value( it, market_price ); if( ( is_npc && np.wants_to_sell( it, val, market_price ) ) || np.wants_to_buy( it, val, market_price ) ) { - result.emplace_back( std::move( loc ), val, count ); + result.emplace_back( loc, val, count ); result.back().adjust_values( adjust, fac ); } }; invslice slice = seller.inv.slice(); for( auto &i : slice ) { - check_item( item_location( seller, &i->front() ), i->size() ); + check_item( i->front(), i->size() ); } - if( !seller.weapon.has_flag( "NO_UNWIELD" ) ) { - check_item( item_location( seller, &seller.weapon ), 1 ); + if( !seller.get_weapon().has_flag( "NO_UNWIELD" ) ) { + check_item( &seller.get_weapon(), 1 ); } //nearby items owned by the NPC will only show up in @@ -213,7 +213,7 @@ std::vector npc_trading::init_buying( player &buyer, player &selle void item_pricing::set_values( int ip_count ) { - const item *i_p = loc.get_item(); + const item *i_p = loc; is_container = i_p->is_container() || i_p->is_ammo_container(); vol = i_p->volume(); weight = i_p->weight(); @@ -231,7 +231,7 @@ void item_pricing::set_values( int ip_count ) // faction we're trading with, as that should always be worth face value. void item_pricing::adjust_values( const double adjust, const faction *fac ) { - if( !fac || fac->currency != loc.get_item()->typeId() ) { + if( !fac || fac->currency != loc->typeId() ) { price *= adjust; } } @@ -274,13 +274,13 @@ void trading_window::update_win( npc &np, const std::string &deal ) for( item_pricing &pricing : yours ) { if( pricing.selected ) { - added.push_back( pricing.loc.get_item() ); + added.push_back( pricing.loc ); } } for( item_pricing &pricing : theirs ) { if( pricing.selected ) { - without.insert( pricing.loc.get_item() ); + without.insert( pricing.loc ); } } @@ -350,14 +350,14 @@ void trading_window::update_win( npc &np, const std::string &deal ) win_w -= 2; for( size_t i = offset; i < list.size() && i < entries_per_page + offset; i++ ) { const item_pricing &ip = list[i]; - const item *it = ip.loc.get_item(); - auto color = it == &person.weapon ? c_yellow : c_light_gray; + const item *it = ip.loc; + auto color = it == &person.get_weapon() ? c_yellow : c_light_gray; const int &owner_sells = they ? ip.u_has : ip.npc_has; const int &owner_sells_charge = they ? ip.u_charges : ip.npc_charges; std::string itname = it->display_name(); - if( np.will_exchange_items_freely() && ip.loc.where() != item_location::type::character ) { - itname = itname + " (" + ip.loc.describe( &g->u ) + ")"; + if( np.will_exchange_items_freely() && ip.loc->where() != item::item_location_type::character ) { + itname = itname + " (" + ip.loc->describe_location( &g->u ) + ")"; color = c_light_blue; } @@ -453,7 +453,7 @@ void trading_window::show_item_data( size_t offset, help += offset; if( help < target_list.size() ) { std::vector info; - const item &itm = *target_list[help].loc.get_item(); + const item &itm = *target_list[help].loc; itm.info( true, info ); item_info_data data( itm.tname(), itm.type_name(), @@ -630,7 +630,7 @@ bool trading_window::perform_trade( npc &np, const std::string &deal ) } } else if( ip.charges > 0 ) { int hint = calc_amount_hint(); - change_amount = get_var_trade( *ip.loc.get_item(), ip.charges, hint ); + change_amount = get_var_trade( *ip.loc, ip.charges, hint ); if( change_amount < 1 ) { continue; } @@ -638,7 +638,7 @@ bool trading_window::perform_trade( npc &np, const std::string &deal ) } else { if( ip.count > 1 ) { int hint = calc_amount_hint(); - change_amount = get_var_trade( *ip.loc.get_item(), ip.count, hint ); + change_amount = get_var_trade( *ip.loc, ip.count, hint ); if( change_amount < 1 ) { continue; } @@ -653,7 +653,7 @@ bool trading_window::perform_trade( npc &np, const std::string &deal ) if( !np.will_exchange_items_freely() ) { your_balance -= delta_price; } - if( ip.loc.where() == item_location::type::character ) { + if( ip.loc->where() == item::item_location_type::character ) { volume_left += ip.vol * change_amount; weight_left += ip.weight * change_amount; } @@ -710,28 +710,30 @@ bool npc_trading::trade( npc &np, int cost, const std::string &deal ) if( traded ) { int practice = 0; - std::list from_map; + std::list from_map; npc_trading::transfer_items( trade_win.yours, g->u, np, from_map, false ); npc_trading::transfer_items( trade_win.theirs, np, g->u, from_map, true ); - for( item_location *loc_ptr : from_map ) { + for( item *loc_ptr : from_map ) { if( !loc_ptr ) { continue; } - item *it = loc_ptr->get_item(); + item *it = loc_ptr; if( !it ) { continue; } if( it->has_var( "trade_charges" ) && it->count_by_charges() ) { it->charges -= static_cast( it->get_var( "trade_charges", 0 ) ); if( it->charges <= 0 ) { - loc_ptr->remove_item(); + loc_ptr->detach(); + loc_ptr->destroy(); } else { it->erase_var( "trade_charges" ); } } else if( it->has_var( "trade_amount" ) ) { - loc_ptr->remove_item(); + loc_ptr->detach(); + loc_ptr->destroy(); } } diff --git a/src/npctrade.h b/src/npctrade.h index e850d0a99205..274b50864065 100644 --- a/src/npctrade.h +++ b/src/npctrade.h @@ -26,17 +26,17 @@ class ui_adaptor; class item_pricing { public: - item_pricing( Character &c, item &it, int v, int count ) : loc( c, &it ), price( v ) { + item_pricing( item &it, int v, int count ) : loc( &it ), price( v ) { set_values( count ); } - item_pricing( item_location &&l, int v, int count ) : loc( std::move( l ) ), price( v ) { + item_pricing( item*l, int v, int count ) : loc( l ), price( v ) { set_values( count ); } void set_values( int ip_count ); void adjust_values( double adjust, const faction *fac ); - item_location loc; + item* loc; int price; // Whether this is selected for trading bool selected = false; @@ -92,7 +92,7 @@ bool pay_npc( npc &np, int cost ); int cash_to_favor( const npc &, int cash ); void transfer_items( std::vector &stuff, player &giver, player &receiver, - std::list &from_map, bool npc_gives ); + std::list &from_map, bool npc_gives ); double net_price_adjustment( const player &buyer, const player &seller ); bool trade( npc &p, int cost, const std::string &deal ); std::vector init_selling( npc &p ); diff --git a/src/panels.cpp b/src/panels.cpp index b27909a0d324..31640d992d33 100644 --- a/src/panels.cpp +++ b/src/panels.cpp @@ -574,10 +574,10 @@ static std::pair temp_stat( const avatar &u ) static std::string get_armor( const avatar &u, body_part bp, unsigned int truncate = 0 ) { - for( std::list::const_iterator it = u.worn.end(); it != u.worn.begin(); ) { + for( ItemList::const_iterator it = u.worn.end(); it != u.worn.begin(); ) { --it; - if( it->covers( bp ) ) { - return it->tname( 1, true, truncate ); + if( ( *it )->covers( bp ) ) { + return ( *it )->tname( 1, true, truncate ); } } return "-"; diff --git a/src/pickup.cpp b/src/pickup.cpp index 55be2d4f7479..c231211cd439 100644 --- a/src/pickup.cpp +++ b/src/pickup.cpp @@ -60,7 +60,7 @@ #include "vehicle_selector.h" #include "vpart_position.h" -using item_count = std::pair; +using item_count = std::pair; using pickup_map = std::map; static void show_pickup_message( const pickup_map &mapPickup ); @@ -85,10 +85,10 @@ static bool select_autopickup_items( const std::vectorvolume() / units::legacy_volume_factor == static_cast( rounded_volume ) ) { + item *begin = *here[i].front(); + if( begin->volume() / units::legacy_volume_factor == static_cast( rounded_volume ) ) { num_checked++; - const std::string item_name = begin_iterator->tname( 1, false ); + const std::string item_name = begin->tname( 1, false ); //Check the Pickup Rules if( get_auto_pickup().check_item( item_name ) == RULE_WHITELISTED ) { @@ -96,7 +96,7 @@ static bool select_autopickup_items( const std::vector( "AUTO_PICKUP_WEIGHT_LIMIT" ); int volume_limit = get_option( "AUTO_PICKUP_VOL_LIMIT" ); if( weight_limit && volume_limit ) { - if( begin_iterator->volume() <= units::from_milliliter( volume_limit * 50 ) && - begin_iterator->weight() <= weight_limit * 50_gram && + if( begin->volume() <= units::from_milliliter( volume_limit * 50 ) && + begin->weight() <= weight_limit * 50_gram && get_auto_pickup().check_item( item_name ) != RULE_BLACKLISTED ) { do_pickup = true; } @@ -153,8 +153,8 @@ static pickup_answer handle_problematic_pickup( const item &it, bool &offered_sw offered_swap = true; // TODO: Gray out if not enough hands if( u.is_armed() ) { - amenu.addentry( WIELD, !u.weapon.has_flag( "NO_UNWIELD" ), 'w', - _( "Dispose of %s and wield %s" ), u.weapon.display_name(), + amenu.addentry( WIELD, !u.get_weapon().has_flag( "NO_UNWIELD" ), 'w', + _( "Dispose of %s and wield %s" ), u.get_weapon().display_name(), it.display_name() ); } else { amenu.addentry( WIELD, true, 'w', _( "Wield %s" ), it.display_name() ); @@ -234,18 +234,18 @@ static bool pick_one_up( pickup::pick_drop_selection &selection, bool &got_water int moves_taken = 100; bool picked_up = false; pickup_answer option = CANCEL; - - item_location &loc = selection.target; + //TODO!: check all these weird copies + item *loc = &*selection.target; // We already checked in do_pickup if this was a nullptr // Make copies so the original remains untouched if we bail out - item_location newloc = loc; + item* newloc = loc; //original item reference - item &it = *newloc.get_item(); + item &it = *newloc; //new item (copy) - item newit = it; - item leftovers = newit; + item& newit = it; + item& leftovers = newit; const cata::optional &quantity = selection.quantity; - std::vector &children = selection.children; + std::vector> &children = selection.children; if( !newit.is_owned_by( g->u, true ) ) { // Has the player given input on if stealing is ok? @@ -279,11 +279,11 @@ static bool pick_one_up( pickup::pick_drop_selection &selection, bool &got_water newit.charges = u.i_add_to_container( newit, false ); units::volume children_volume = std::accumulate( children.begin(), children.end(), 0_ml, - []( units::volume acc, const item_location & c ) { + []( units::volume acc, const safe_reference & c ) { return acc + c->volume(); } ); units::mass children_weight = std::accumulate( children.begin(), children.end(), 0_gram, - []( units::mass acc, const item_location & c ) { + []( units::mass acc, const safe_reference & c ) { return acc + c->weight(); } ); @@ -338,13 +338,13 @@ static bool pick_one_up( pickup::pick_drop_selection &selection, bool &got_water //using original item, possibly modifying it picked_up = u.wield( it ); if( picked_up ) { - u.weapon.charges = newit.charges; + u.get_weapon().charges = newit.charges; } - if( u.weapon.invlet ) { - add_msg( m_info, _( "Wielding %c - %s" ), u.weapon.invlet, - u.weapon.display_name() ); + if( u.get_weapon().invlet ) { + add_msg( m_info, _( "Wielding %c - %s" ), u.get_weapon().invlet, + u.get_weapon().display_name() ); } else { - add_msg( m_info, _( "Wielding - %s" ), u.weapon.display_name() ); + add_msg( m_info, _( "Wielding - %s" ), u.get_weapon().display_name() ); } } else { add_msg( m_neutral, "%s", wield_check.c_str() ); @@ -366,7 +366,7 @@ static bool pick_one_up( pickup::pick_drop_selection &selection, bool &got_water case STASH: auto &entry = map_pickup[newit.tname()]; entry.second += newit.count(); - entry.first = u.i_add( newit ); + entry.first = &u.i_add( newit ); picked_up = true; break; } @@ -374,22 +374,23 @@ static bool pick_one_up( pickup::pick_drop_selection &selection, bool &got_water if( picked_up ) { // Children have to be picked up first, since removing parent would re-index the stack if( option != EMPTY ) { - for( item_location &child_loc : children ) { + for( safe_reference &child_loc : children ) { item &added = u.i_add( *child_loc ); auto &pickup_entry = map_pickup[added.tname()]; - pickup_entry.first = added; + pickup_entry.first = &added; pickup_entry.second += added.count(); - child_loc.remove_item(); + child_loc->detach(); } } // If we picked up a whole stack, remove the original item // Otherwise, replace the item with the leftovers if( leftovers.charges > 0 ) { - *loc.get_item() = std::move( leftovers ); + //TODO!: restore next, need to work out what's actually changed here, it's probably just charges + //*loc.get_item() = std::move( leftovers ); } else { - loc.remove_item(); + loc->detach(); } u.moves -= moves_taken; @@ -451,9 +452,9 @@ static std::vector> calculate_parents( std::vector> parents( stacked_here.size() ); if( !stacked_here.empty() ) { size_t last_parent_index = 0; - item_drop_token last_parent_token = *stacked_here.front().front()->drop_token; + item_drop_token last_parent_token = *( *stacked_here.front().front() )->drop_token; for( size_t i = 1; i < stacked_here.size(); i++ ) { - auto item_iter = stacked_here[i].front(); + auto item_iter = *stacked_here[i].front(); const item_drop_token &this_token = *item_iter->drop_token; if( this_token.is_child_of( last_parent_token ) ) { parents[i] = last_parent_index; @@ -485,7 +486,7 @@ std::vector stack_for_pickup_ui( const std::map, parent_child_check_t> parent_child_check; // First, we need to check which parent-child groups exist for( item_stack::iterator it : unstacked ) { - const auto &token = *it->drop_token; + const auto &token = *( *it )->drop_token; if( token.drop_number > 0 ) { std::pair turn_and_drop = std::make_pair( token.turn, token.drop_number ); parent_child_check[turn_and_drop].parent_exists = true; @@ -499,7 +500,7 @@ std::vector stack_for_pickup_ui( const // Second pass: we group children and parents together, but only if both sides are known to exist std::map, unstacked_items> children_by_parent; for( item_stack::iterator it : unstacked ) { - const auto &token = *it->drop_token; + const auto &token = *( *it )->drop_token; std::pair turn_and_drop = std::make_pair( token.turn, token.drop_number ); if( token.drop_number > 0 && parent_child_check[turn_and_drop].child_exists ) { children_by_parent[turn_and_drop].parent = it; @@ -521,8 +522,8 @@ std::vector stack_for_pickup_ui( const for( item_stack::iterator it : pr.second.unstacked_children ) { bool found_stack = false; for( std::list &stack : restacked_children ) { - const item &stack_top = *stack.front(); - if( stack_top.display_stacked_with( *it ) ) { + const item &stack_top = **stack.front(); + if( stack_top.display_stacked_with( **it ) ) { stack.push_back( it ); found_stack = true; break; @@ -610,8 +611,8 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) // but non-frozen water. if( ( !isEmpty ) && g->m.furn( p ) == f_toilet ) { isEmpty = true; - for( const item &maybe_water : g->m.i_at( p ) ) { - if( maybe_water.typeId() != itype_id( "water" ) ) { + for( const item * const &maybe_water : g->m.i_at( p ) ) { + if( maybe_water->typeId() != itype_id( "water" ) ) { isEmpty = false; break; } @@ -663,12 +664,12 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) if( static_cast( here.size() ) <= min && min != -1 ) { if( from_vehicle ) { g->u.assign_activity( player_activity( pickup_activity_actor( - { { item_location( vehicle_cursor( *veh, cargo_part ), &*here.front() ), cata::nullopt, {} } }, + { { *here.front(), cata::nullopt, {} } }, cata::nullopt ) ) ); } else { g->u.assign_activity( player_activity( pickup_activity_actor( - { { item_location( map_cursor( p ), &*here.front() ), cata::nullopt, {} } }, + { { *here.front(), cata::nullopt, {} } }, g->u.pos() ) ) ); } @@ -723,7 +724,7 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) //find max length of item name and resize pickup window width for( const std::list &cur_list : stacked_here ) { - const item &this_item = *cur_list.front(); + const item &this_item = **cur_list.front(); const int item_len = utf8_width( remove_color_tags( this_item.display_name() ) ) + 10; if( item_len > pickupW && item_len < TERMX ) { pickupW = item_len; @@ -788,7 +789,7 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) const std::string all_pickup_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:;"; ui.on_redraw( [&]( const ui_adaptor & ) { - const item &selected_item = *stacked_here[matches[selected]].front(); + const item &selected_item = **stacked_here[matches[selected]].front(); if( selected >= 0 && selected <= static_cast( stacked_here.size() ) - 1 ) { std::vector vThisItem; @@ -818,7 +819,7 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) for( int cur_it = start; cur_it < start + maxitems; cur_it++ ) { if( cur_it < static_cast( matches.size() ) ) { int true_it = matches[cur_it]; - const item &this_item = *stacked_here[true_it].front(); + const item &this_item = **stacked_here[true_it].front(); nc_color icolor = this_item.color_in_inventory(); if( cur_it == selected ) { icolor = hilite( c_white ); @@ -858,16 +859,16 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) wprintw( w_pickup, "- " ); } std::string item_name; - if( stacked_here[true_it].front()->is_money() ) { + if( ( *stacked_here[true_it].front() )->is_money() ) { //Count charges // TODO: transition to the item_location system used for the inventory unsigned int charges_total = 0; for( const item_stack::iterator &it : stacked_here[true_it] ) { - charges_total += it->charges; + charges_total += ( *it )->charges; } //Picking up none or all the cards in a stack if( !getitem[true_it].pick || !getitem[true_it].count ) { - item_name = stacked_here[true_it].front()->display_money( stacked_here[true_it].size(), + item_name = ( *stacked_here[true_it].front() )->display_money( stacked_here[true_it].size(), charges_total ); } else { unsigned int charges = 0; @@ -875,10 +876,10 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) int c = item_count; for( std::list::const_iterator it = stacked_here[true_it].begin(); it != stacked_here[true_it].end() && c > 0; ++it, --c ) { - charges += ( *it )->charges; + charges += ( **it )->charges; } - item_name = stacked_here[true_it].front()->display_money( item_count, charges_total, charges ); + item_name = ( *stacked_here[true_it].front() )->display_money( item_count, charges_total, charges ); } } else { item_name = this_item.display_name( stacked_here[true_it].size() ); @@ -1051,7 +1052,7 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) size_t true_idx = matches[idx]; pickup_count &selected_stack = getitem[true_idx]; if( itemcount || selected_stack.count ) { - const item &temp = *stacked_here[true_idx].front(); + const item &temp = **stacked_here[true_idx].front(); int amount_available = temp.count_by_charges() ? temp.charges : stacked_here[true_idx].size(); if( itemcount && *itemcount >= amount_available ) { itemcount.reset(); @@ -1098,7 +1099,7 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) while( matches.empty() ) { auto filter_func = item_filter_from_string( new_filter ); for( size_t index = 0; index < stacked_here.size(); index++ ) { - if( filter_func( *stacked_here[index].front() ) ) { + if( filter_func( **stacked_here[index].front() ) ) { matches.push_back( index ); } } @@ -1134,7 +1135,7 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) for( size_t i = 0; i < getitem.size(); i++ ) { if( getitem[i].pick ) { // Make a copy for calculating weight/volume - item temp = *stacked_here[i].front(); + item &temp = **stacked_here[i].front(); if( temp.count_by_charges() && getitem[i].count && *getitem[i].count < temp.charges ) { temp.charges = *getitem[i].count; } @@ -1186,8 +1187,8 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) break; } - if( it->count_by_charges() ) { - int num_picked = std::min( it->charges, count ); + if( ( *it )->count_by_charges() ) { + int num_picked = std::min( ( *it )->charges, count ); pick_values.emplace_back( it, num_picked ); count -= num_picked; } else { @@ -1197,16 +1198,11 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) } } - std::vector locations; + std::vector locations; std::vector quantities; for( std::pair &iter_qty : pick_values ) { - item_location loc; - if( from_vehicle ) { - loc = item_location( vehicle_cursor( *veh, cargo_part ), &*iter_qty.first ); - } else { - loc = item_location( map_cursor( p ), &*iter_qty.first ); - } + item*loc=*iter_qty.first; locations.push_back( loc ); quantities.push_back( iter_qty.second ); } @@ -1225,12 +1221,12 @@ void pickup::pick_up( const tripoint &p, int min, from_where get_items_from ) void show_pickup_message( const pickup_map &mapPickup ) { for( auto &entry : mapPickup ) { - if( entry.second.first.invlet != 0 ) { + if( entry.second.first->invlet != 0 ) { add_msg( _( "You pick up: %d %s [%c]" ), entry.second.second, - entry.second.first.display_name( entry.second.second ), entry.second.first.invlet ); + entry.second.first->display_name( entry.second.second ), entry.second.first->invlet ); } else { add_msg( _( "You pick up: %d %s" ), entry.second.second, - entry.second.first.display_name( entry.second.second ) ); + entry.second.first->display_name( entry.second.second ) ); } } } @@ -1302,7 +1298,7 @@ void pick_drop_selection::deserialize( JsonIn &jin ) jo.read( "children", children ); } -std::vector optimize_pickup( const std::vector &targets, +std::vector optimize_pickup( const std::vector &targets, const std::vector &quantities ) { // This is essentially legacy code handling, so checks are good design @@ -1314,7 +1310,7 @@ std::vector optimize_pickup( const std::vector optimized; for( size_t i = 0; i < targets.size(); i++ ) { - const item_location &loc = targets[i]; + item*loc = targets[i]; // If it was possible, the two locations should be required to be consecutive if( loc->drop_token->is_child_of( last_token ) ) { optimized.back().children.emplace_back( loc ); diff --git a/src/pickup.h b/src/pickup.h index fd4354a82037..9075c1ca0784 100644 --- a/src/pickup.h +++ b/src/pickup.h @@ -5,7 +5,6 @@ #include class item; -class item_location; class Character; class JsonIn; class JsonOut; diff --git a/src/pickup_token.h b/src/pickup_token.h index 53261b369e6a..1b292179128f 100644 --- a/src/pickup_token.h +++ b/src/pickup_token.h @@ -8,6 +8,7 @@ #include "item_location.h" #include "item_stack.h" #include "optional.h" +#include "safe_reference.h" class JsonIn; class JsonOut; @@ -18,15 +19,15 @@ namespace pickup /** Activity-associated item */ struct act_item { /// inventory item - item_location loc; + safe_reference loc; /// How many items need to be processed int count = 0; /// Amount of moves that processing will consume int consumed_moves = 0; act_item() = default; - act_item( const item_location &loc, int count, int consumed_moves ) - : loc( loc ), + act_item( item &loc, int count, int consumed_moves ) + : loc( &loc ), count( count ), consumed_moves( consumed_moves ) {} @@ -35,9 +36,9 @@ struct act_item { }; struct pick_drop_selection { - item_location target; + safe_reference target; cata::optional quantity; - std::vector children; + std::vector> children; void serialize( JsonOut &jsout ) const; void deserialize( JsonIn &jin ); @@ -50,10 +51,10 @@ struct stacked_items { // TODO: This should get information on whether children are consecutive /** Finds possible parent-child relations in picked up items to save moves */ -std::vector optimize_pickup( const std::vector &targets, +std::vector optimize_pickup( const std::vector &targets, const std::vector &quantities ); std::list reorder_for_dropping( Character &p, const drop_locations &drop ); -std::list obtain_and_tokenize_items( player &p, std::list &items ); +ItemList obtain_and_tokenize_items( player &p, std::list &items ); std::vector stack_for_pickup_ui( const std::vector &unstacked ); // TODO: This probably shouldn't return raw iterators diff --git a/src/player.cpp b/src/player.cpp index a4e14e7cc1b5..03ed9aff27ae 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -567,24 +567,24 @@ void player::mod_stat( const std::string &stat, float modifier ) } } -std::list player::get_artifact_items() +std::vector player::get_artifact_items() { - std::list art_items; + std::vector art_items; const invslice &stacks = inv.slice(); for( auto &stack : stacks ) { - item &stack_iter = stack->front(); + item &stack_iter = *stack->front(); if( stack_iter.is_artifact() ) { art_items.push_back( &stack_iter ); } } for( auto &elem : worn ) { - if( elem.is_artifact() ) { - art_items.push_back( &elem ); + if( elem->is_artifact() ) { + art_items.push_back( elem ); } } if( is_armed() ) { - if( weapon.is_artifact() ) { - art_items.push_back( &weapon ); + if( get_weapon().is_artifact() ) { + art_items.push_back( &get_weapon() ); } } return art_items; @@ -755,12 +755,12 @@ int player::intimidation() const { /** @EFFECT_STR increases intimidation factor */ int ret = get_str() * 2; - if( weapon.is_gun() ) { + if( get_weapon().is_gun() ) { ret += 10; } - if( weapon.damage_melee( DT_BASH ) >= 12 || - weapon.damage_melee( DT_CUT ) >= 12 || - weapon.damage_melee( DT_STAB ) >= 12 ) { + if( get_weapon().damage_melee( DT_BASH ) >= 12 || + get_weapon().damage_melee( DT_CUT ) >= 12 || + get_weapon().damage_melee( DT_STAB ) >= 12 ) { ret += 5; } @@ -789,8 +789,8 @@ void player::on_dodge( Creature *source, float difficulty ) dodges_left--; // dodging throws of our aim unless we are either skilled at dodging or using a small weapon - if( is_armed() && weapon.is_gun() ) { - recoil += std::max( weapon.volume() / 250_ml - get_skill_level( skill_dodge ), 0 ) * rng( 0, 100 ); + if( is_armed() && get_weapon().is_gun() ) { + recoil += std::max( get_weapon().volume() / 250_ml - get_skill_level( skill_dodge ), 0 ) * rng( 0, 100 ); recoil = std::min( MAX_RECOIL, recoil ); } @@ -933,9 +933,9 @@ bool player::immune_to( body_part bp, damage_unit dam ) const passive_absorb_hit( convert_bp( bp ).id(), dam ); - for( const item &cloth : worn ) { - if( cloth.get_coverage() == 100 && cloth.covers( bp ) ) { - cloth.mitigate_damage( dam ); + for( const item * const &cloth : worn ) { + if( cloth->get_coverage() == 100 && cloth->covers( bp ) ) { + cloth->mitigate_damage( dam ); } } @@ -1627,15 +1627,14 @@ void player::update_body_wetness( const w_point &weather ) // TODO: Make clothing slow down drying } -void player::on_worn_item_transform( const item &old_it, const item &new_it ) -{ - morale->on_worn_item_transform( old_it, new_it ); -} - void player::process_items() { - if( weapon.needs_processing() && weapon.process( this, pos(), false ) ) { - weapon = item(); + if( get_weapon().needs_processing() && get_weapon().process( this, pos(), false ) ) { + //TODO!: check + item &it=get_weapon(); + it.detach(); + it.destroy(); + set_weapon(null_item_reference()); } std::vector inv_active = inv.active_items(); @@ -1652,8 +1651,8 @@ void player::process_items() // Active item processing done, now we're recharging. std::vector active_worn_items; - bool weapon_active = weapon.has_flag( "USE_UPS" ) && - weapon.charges < weapon.type->maximum_charges(); + bool weapon_active = get_weapon().has_flag( "USE_UPS" ) && + get_weapon().charges < get_weapon().type->maximum_charges(); std::vector active_held_items; int ch_UPS = 0; for( size_t index = 0; index < inv.size(); index++ ) { @@ -1669,22 +1668,22 @@ void player::process_items() } } bool update_required = get_check_encumbrance(); - for( item &w : worn ) { - if( w.has_flag( "USE_UPS" ) && - w.charges < w.type->maximum_charges() ) { - active_worn_items.push_back( &w ); + for( item * const &w : worn ) { + if( w->has_flag( "USE_UPS" ) && + w->charges < w->type->maximum_charges() ) { + active_worn_items.push_back( w ); } // Necessary for UPS in Aftershock - check worn items for charge - const itype_id &identifier = w.typeId(); + const itype_id &identifier = w->typeId(); if( identifier == itype_UPS_off ) { - ch_UPS += w.ammo_remaining(); + ch_UPS += w->ammo_remaining(); } else if( identifier == itype_adv_UPS_off ) { - ch_UPS += w.ammo_remaining() / 0.6; + ch_UPS += w->ammo_remaining() / 0.6; } - if( !update_required && w.encumbrance_update_ ) { + if( !update_required && w->encumbrance_update_ ) { update_required = true; } - w.encumbrance_update_ = false; + w->encumbrance_update_ = false; } if( update_required ) { reset_encumbrance(); @@ -1706,7 +1705,7 @@ void player::process_items() } if( weapon_active && ch_UPS_used < ch_UPS ) { ch_UPS_used++; - weapon.charges++; + get_weapon().charges++; } for( item *worn_item : active_worn_items ) { if( ch_UPS_used >= ch_UPS ) { @@ -1719,35 +1718,35 @@ void player::process_items() use_charges( itype_UPS, ch_UPS_used ); } } - -item player::reduce_charges( int position, int quantity ) +//TODO!: check the pointers on this: +item *player::reduce_charges( int position, int quantity ) { item &it = i_at( position ); if( it.is_null() ) { debugmsg( "invalid item position %d for reduce_charges", position ); - return item(); + return &null_item_reference(); } if( it.charges <= quantity ) { - return i_rem( position ); + return &i_rem( position ); } it.mod_charges( -quantity ); - item tmp( it ); - tmp.charges = quantity; + item *tmp = item::spawn( it ); + tmp->charges = quantity; return tmp; } - -item player::reduce_charges( item *it, int quantity ) +//TODO!: check the pointers on this: +item *player::reduce_charges( item *it, int quantity ) { if( !has_item( *it ) ) { debugmsg( "invalid item (name %s) for reduce_charges", it->tname() ); - return item(); + return &null_item_reference(); } if( it->charges <= quantity ) { - return i_rem( it ); + return &i_rem( it ); } it->mod_charges( -quantity ); - item result( *it ); - result.charges = quantity; + item *result = item::spawn( *it ); + result->charges = quantity; return result; } @@ -1772,10 +1771,10 @@ int player::drink_from_hands( item &water ) if( query_yn( _( "Drink %s from your hands?" ), colorize( water.type_name(), water.color_in_inventory() ) ) ) { // Create a dose of water no greater than the amount of water remaining. - item water_temp( water ); + item* water_temp=item::spawn_temporary( water ); // If player is slaked water might not get consumed. - consume_item( water_temp ); - charges_consumed = water.charges - water_temp.charges; + consume_item( *water_temp ); + charges_consumed = water.charges - water_temp->charges; if( charges_consumed > 0 ) { moves -= 350; } @@ -1902,9 +1901,9 @@ bool player::consume_item( item &target ) return false; } -bool player::consume( item_location loc ) +bool player::consume( item& loc ) { - item &target = *loc; + item &target = loc; const bool wielding = is_wielding( target ); const bool worn = is_worn( target ); const bool inv_item = !( wielding || worn ); @@ -1925,7 +1924,7 @@ bool player::consume( item_location loc ) } if( was_in_container && wielding ) { - add_msg_if_player( _( "You are now wielding an empty %s." ), weapon.tname() ); + add_msg_if_player( _( "You are now wielding an empty %s." ), get_weapon().tname() ); } else if( was_in_container && worn ) { add_msg_if_player( _( "You are now wearing an empty %s." ), target.tname() ); } else if( was_in_container && !is_npc() ) { @@ -1939,7 +1938,7 @@ bool player::consume( item_location loc ) } if( drop_it ) { add_msg( _( "You drop the empty %s." ), target.tname() ); - put_into_vehicle_or_drop( *this, item_drop_reason::deliberate, { inv.remove_item( &target ) } ); + put_into_vehicle_or_drop( *this, item_drop_reason::deliberate, { &inv.remove_item( &target ) } ); } else { int quantity = inv.const_stack( inv.position_by_item( &target ) ).size(); char letter = target.invlet ? target.invlet : ' '; @@ -2040,7 +2039,6 @@ item::reload_option player::select_ammo( const item &base, return e.ammo->contents.front().display_name(); } else { - const_cast( ammo_location ).make_dirty(); return ( ammo_location && ammo_location == e.ammo ? "* " : "" ) + e.ammo->display_name(); } } ); @@ -2054,9 +2052,9 @@ item::reload_option player::select_ammo( const item &base, if( is_ammo_container && is_worn( *e.ammo ) ) { return e.ammo->type_name(); } - return string_format( _( "%s, %s" ), e.ammo->type_name(), e.ammo.describe( &g->u ) ); + return string_format( _( "%s, %s" ), e.ammo->type_name(), e.ammo->describe_location( &g->u ) ); } - return e.ammo.describe( &g->u ); + return e.ammo->describe_location( &g->u ); } ); // Pads elements to match longest member and return length @@ -2232,7 +2230,7 @@ item::reload_option player::select_ammo( const item &base, return item::reload_option(); } - const item_location &sel = opts[ menu.ret ].ammo; + item* sel = opts[ menu.ret ].ammo; uistate.lastreload[ ammotype( base.ammo_default().str() ) ] = sel->is_ammo_container() ? sel->contents.front().typeId() : sel->typeId(); @@ -2258,7 +2256,7 @@ bool player::list_ammo( const item &base, std::vector &ammo bool ammo_match_found = false; int ammo_search_range = is_mounted() ? -1 : 1; for( const auto e : opts ) { - for( const item_location &ammo : find_ammo( *e, include_empty_mags, ammo_search_range ) ) { + for( const item*ammo : find_ammo( *e, include_empty_mags, ammo_search_range ) ) { // don't try to unload frozen liquids if( ammo->is_watertight_container() && ammo->contents_made_of( SOLID ) ) { continue; @@ -2352,7 +2350,7 @@ ret_val player::can_wield( const item &it ) const _( "You need at least one arm to even consider wielding something." ) ); } - if( is_armed() && weapon.has_flag( "NO_UNWIELD" ) ) { + if( is_armed() && get_weapon().has_flag( "NO_UNWIELD" ) ) { return ret_val::make_failure( _( "The %s is preventing you from wielding the %s." ), weapname(), it.tname() ); } @@ -2382,17 +2380,17 @@ ret_val player::can_wield( const item &it ) const bool player::unwield() { - if( weapon.is_null() ) { + if( get_weapon().is_null() ) { return true; } - if( !can_unwield( weapon ).success() ) { + if( !can_unwield( get_weapon() ).success() ) { return false; } - const std::string query = string_format( _( "Stop wielding %s?" ), weapon.tname() ); + const std::string query = string_format( _( "Stop wielding %s?" ), get_weapon().tname() ); - if( !dispose_item( item_location( *this, &weapon ), query ) ) { + if( !dispose_item( get_weapon(), query ) ) { return false; } @@ -2504,28 +2502,28 @@ bool player::can_reload( const item &it, const itype_id &ammo ) const return true; } -void player::mend_item( item_location &&obj, bool interactive ) +void player::mend_item( item& obj, bool interactive ) { if( has_trait( trait_DEBUG_HS ) ) { uilist menu; menu.text = _( "Toggle which fault?" ); std::vector> opts; - for( const auto &f : obj->faults_potential() ) { - opts.emplace_back( f, !!obj->faults.count( f ) ); + for( const auto &f : obj.faults_potential() ) { + opts.emplace_back( f, !!obj.faults.count( f ) ); menu.addentry( -1, true, -1, string_format( opts.back().second ? pgettext( "fault", "Mend: %s" ) : pgettext( "fault", "Set: %s" ), f.obj().name() ) ); } if( opts.empty() ) { - add_msg( m_info, _( "The %s doesn't have any faults to toggle." ), obj->tname() ); + add_msg( m_info, _( "The %s doesn't have any faults to toggle." ), obj.tname() ); return; } menu.query(); if( menu.ret >= 0 ) { if( opts[ menu.ret ].second ) { - obj->faults.erase( opts[ menu.ret ].first ); + obj.faults.erase( opts[ menu.ret ].first ); } else { - obj->faults.insert( opts[ menu.ret ].first ); + obj.faults.insert( opts[ menu.ret ].first ); } } return; @@ -2540,7 +2538,7 @@ void player::mend_item( item_location &&obj, bool interactive ) }; std::vector mending_options; - for( const fault_id &f : obj->faults ) { + for( const fault_id &f : obj.faults ) { for( const auto &m : f->mending_methods() ) { mending_option opt { f, m.second, true }; for( const auto &sk : m.second.skills ) { @@ -2557,9 +2555,9 @@ void player::mend_item( item_location &&obj, bool interactive ) if( mending_options.empty() ) { if( interactive ) { - add_msg( m_info, _( "The %s doesn't have any faults to mend." ), obj->tname() ); - if( obj->damage() > 0 ) { - const std::set &rep = obj->repaired_with(); + add_msg( m_info, _( "The %s doesn't have any faults to mend." ), obj.tname() ); + if( obj.damage() > 0 ) { + const std::set &rep = obj.repaired_with(); if( !rep.empty() ) { const std::string repair_options = enumerate_as_string( rep.begin(), rep.end(), []( const itype_id & e ) { @@ -2647,7 +2645,7 @@ void player::mend_item( item_location &&obj, bool interactive ) const mending_option &opt = mending_options[sel]; if( !opt.doable ) { if( interactive ) { - add_msg( m_info, _( "You are currently unable to mend the %s this way." ), obj->tname() ); + add_msg( m_info, _( "You are currently unable to mend the %s this way." ), obj.tname() ); } return; } @@ -2656,7 +2654,7 @@ void player::mend_item( item_location &&obj, bool interactive ) assign_activity( activity_id( "ACT_MEND_ITEM" ), to_moves( method.time ) ); activity.name = opt.fault.str(); activity.str_values.emplace_back( method.id ); - activity.targets.push_back( std::move( obj ) ); + activity.targets.push_back( obj ); } } @@ -2672,16 +2670,15 @@ int player::item_reload_cost( const item &it, const item &ammo, int qty ) const debugmsg( "cannot determine reload cost as %s is neither ammo or magazine", ammo.tname() ); return 0; } - + //TODO!: check // If necessary create duplicate with appropriate number of charges - item obj = ammo; - obj = obj.split( qty ); - if( obj.is_null() ) { - obj = ammo; + item *obj = &ammo.split( qty ); + if( obj->is_null() ) { + obj = &ammo; } // No base cost for handling ammo - that's already included in obtain cost // We have the ammo in our hands right now - int mv = item_handling_cost( obj, true, 0 ); + int mv = item_handling_cost( *obj, true, 0 ); if( ammo.has_flag( "MAG_BULKY" ) ) { mv *= 1.5; // bulky magazines take longer to insert @@ -2711,15 +2708,16 @@ int player::item_reload_cost( const item &it, const item &ammo, int qty ) const return std::max( mv, 25 ); } -cata::optional::iterator> +cata::optional player::wear( int pos, bool interactive ) { return wear( i_at( pos ), interactive ); } -cata::optional::iterator> +cata::optional player::wear( item &to_wear, bool interactive ) { + //TODO!: alla this if( is_worn( to_wear ) ) { if( interactive ) { add_msg_player_or_npc( m_info, @@ -2740,8 +2738,8 @@ player::wear( item &to_wear, bool interactive ) bool was_weapon; item to_wear_copy( to_wear ); - if( &to_wear == &weapon ) { - weapon = item(); + if( &to_wear == weapon ) { + weapon = &null_item_reference(); was_weapon = true; } else { inv.remove_item( &to_wear ); @@ -2752,7 +2750,7 @@ player::wear( item &to_wear, bool interactive ) auto result = wear_item( to_wear_copy, interactive ); if( !result ) { if( was_weapon ) { - weapon = to_wear_copy; + weapon = &to_wear_copy; } else { inv.add_item( to_wear_copy, true ); } @@ -2779,10 +2777,10 @@ bool player::can_lift( int lift_strength_required ) const return str + npc_str >= lift_strength_required; } -ret_val player::can_takeoff( const item &it, const std::list *res ) const +ret_val player::can_takeoff( const item &it, const ItemList *res ) const { - auto iter = std::find_if( worn.begin(), worn.end(), [ &it ]( const item & wit ) { - return &it == &wit; + auto iter = std::find_if( worn.begin(), worn.end(), [ &it ]( const item * const & wit ) { + return &it == wit; } ); if( iter == worn.end() ) { @@ -2803,7 +2801,7 @@ ret_val player::can_takeoff( const item &it, const std::list *res ) return ret_val::make_success(); } -bool player::takeoff( item &it, std::list *res ) +bool player::takeoff( item &it, ItemList *res ) { const auto ret = can_takeoff( it, res ); if( !ret.success() ) { @@ -2811,8 +2809,8 @@ bool player::takeoff( item &it, std::list *res ) return false; } - auto iter = std::find_if( worn.begin(), worn.end(), [ &it ]( const item & wit ) { - return &it == &wit; + auto iter = std::find_if( worn.begin(), worn.end(), [ &it ]( const item * const & wit ) { + return &it == wit; } ); if( res == nullptr ) { @@ -2826,11 +2824,11 @@ bool player::takeoff( item &it, std::list *res ) return false; } } - iter->on_takeoff( *this ); + ( *iter )->on_takeoff( *this ); inv.add_item_keep_invlet( it ); } else { - iter->on_takeoff( *this ); - res->push_back( it ); + ( *iter )->on_takeoff( *this ); + res->push_back( &it ); } add_msg_player_or_npc( _( "You take off your %s." ), @@ -2862,9 +2860,9 @@ bool player::add_or_drop_with_msg( item &it, const bool unloading ) if( it.is_ammo() && it.charges == 0 ) { return true; } else if( !this->can_pick_volume( it ) ) { - put_into_vehicle_or_drop( *this, item_drop_reason::too_large, { it } ); + put_into_vehicle_or_drop( *this, item_drop_reason::too_large, { &it } ); } else if( !this->can_pick_weight( it, !get_option( "DANGEROUS_PICKUPS" ) ) ) { - put_into_vehicle_or_drop( *this, item_drop_reason::too_heavy, { it } ); + put_into_vehicle_or_drop( *this, item_drop_reason::too_heavy, { &it } ); } else { auto &ni = this->i_add( it ); add_msg( _( "You put the %s in your inventory." ), ni.tname() ); @@ -3259,7 +3257,7 @@ void player::gunmod_add( item &gun, item &mod ) } // first check at least the minimum requirements are met - if( !has_trait( trait_DEBUG_HS ) && !can_use( mod, gun ) ) { + if( !has_trait( trait_DEBUG_HS ) && !can_use( mod, &gun ) ) { return; } @@ -3356,7 +3354,7 @@ void player::toolmod_add( item_location tool, item_location mod ) return; } // first check at least the minimum requirements are met - if( !has_trait( trait_DEBUG_HS ) && !can_use( *mod, *tool ) ) { + if( !has_trait( trait_DEBUG_HS ) && !can_use( *mod, &*tool ) ) { return; } @@ -3391,7 +3389,7 @@ recipe_subset player::get_recipes_from_books( const inventory &crafting_inv, recipe_subset res; for( const auto &stack : crafting_inv.const_slice() ) { - const item &candidate = stack->front(); + const item &candidate = *stack->front(); for( std::pair recipe_entry : candidate.get_available_recipes( *this ) ) { @@ -3785,13 +3783,13 @@ bool player::has_magazine_for_ammo( const ammotype &at ) const std::string player::weapname() const { - if( weapon.is_gun() ) { - std::string str = string_format( "(%d) [%s] %s", weapon.ammo_remaining(), - weapon.gun_current_mode().tname(), weapon.type_name() ); + if( weapon->is_gun() ) { + std::string str = string_format( "(%d) [%s] %s", weapon->ammo_remaining(), + weapon->gun_current_mode().tname(), weapon->type_name() ); // Is either the base item or at least one auxiliary gunmod loaded (includes empty magazines) - bool base = weapon.ammo_capacity() > 0 && !weapon.has_flag( "RELOAD_AND_SHOOT" ); + bool base = weapon->ammo_capacity() > 0 && !weapon->has_flag( "RELOAD_AND_SHOOT" ); - const auto mods = weapon.gunmods(); + const auto mods = weapon->gunmods(); bool aux = std::any_of( mods.begin(), mods.end(), [&]( const item * e ) { return e->is_gun() && e->ammo_capacity() > 0 && !e->has_flag( "RELOAD_AND_SHOOT" ); } ); @@ -3809,15 +3807,15 @@ std::string player::weapname() const } return str; - } else if( weapon.is_container() && weapon.contents.num_item_stacks() == 1 ) { - return string_format( "%s (%d)", weapon.tname(), - weapon.contents.front().charges ); + } else if( weapon->is_container() && weapon->contents.num_item_stacks() == 1 ) { + return string_format( "%s (%d)", weapon->tname(), + weapon->contents.front().charges ); } else if( !is_armed() ) { return _( "fists" ); } else { - return weapon.tname(); + return weapon->tname(); } } @@ -3827,7 +3825,7 @@ bool player::wield_contents( item &container, item *internal_item, bool penaltie // if index not specified and container has multiple items then ask the player to choose one if( internal_item == nullptr ) { std::vector opts; - std::list container_contents = container.contents.all_items_top(); + std::vector container_contents = container.contents.all_items_top(); std::transform( container_contents.begin(), container_contents.end(), std::back_inserter( opts ), []( const item * elem ) { return elem->display_name(); @@ -3862,14 +3860,14 @@ bool player::wield_contents( item &container, item *internal_item, bool penaltie } inv.unsort(); } - - weapon = std::move( *internal_item ); + //TODO!: chhhecks + weapon = internal_item; container.remove_item( *internal_item ); container.on_contents_changed(); - inv.update_invlet( weapon ); - inv.update_cache_with_item( weapon ); - last_item = weapon.typeId(); + inv.update_invlet( *weapon ); + inv.update_cache_with_item( *weapon ); + last_item = weapon->typeId(); /** * @EFFECT_PISTOL decreases time taken to draw pistols from holsters @@ -3881,12 +3879,12 @@ bool player::wield_contents( item &container, item *internal_item, bool penaltie * @EFFECT_CUTTING decreases time taken to draw cutting weapons from scabbards * @EFFECT_BASHING decreases time taken to draw bashing weapons from holsters */ - int lvl = get_skill_level( weapon.is_gun() ? weapon.gun_skill() : weapon.melee_skill() ); - mv += item_handling_cost( weapon, penalties, base_cost ) / ( ( lvl + 10.0f ) / 10.0f ); + int lvl = get_skill_level( weapon->is_gun() ? weapon->gun_skill() : weapon->melee_skill() ); + mv += item_handling_cost( *weapon, penalties, base_cost ) / ( ( lvl + 10.0f ) / 10.0f ); moves -= mv; - weapon.on_wield( *this, mv ); + weapon->on_wield( *this, mv ); return true; } diff --git a/src/player.h b/src/player.h index 017772d66785..e9ec9a64c67c 100644 --- a/src/player.h +++ b/src/player.h @@ -266,12 +266,12 @@ class player : public Character int hp_percentage() const override; /** Returns list of artifacts in player inventory. **/ - std::list get_artifact_items(); + std::vector get_artifact_items(); /** used for drinking from hands, returns how many charges were consumed */ int drink_from_hands( item &water ); /** Used for eating object at pos, returns true if object is removed from inventory (last charge was consumed) */ - bool consume( item_location loc ); + bool consume( item &loc ); /** Used for eating a particular item that doesn't need to be in inventory. * Returns true if the item is to be removed (doesn't remove). */ bool consume_item( item &target ); @@ -303,7 +303,7 @@ class player : public Character * Check player capable of taking off an item. * @param it Thing to be taken off */ - ret_val can_takeoff( const item &it, const std::list *res = nullptr ) const; + ret_val can_takeoff( const item &it, const ItemList *res = nullptr ) const; /** * Check player capable of wielding an item. @@ -327,7 +327,7 @@ class player : public Character * @param obj Object to mend * @param interactive if true prompts player when multiple faults, otherwise mends the first */ - void mend_item( item_location &&obj, bool interactive = true ); + void mend_item( item &obj, bool interactive = true ); /** * Calculate (but do not deduct) the number of moves required to reload an item with specified quantity of ammo @@ -338,19 +338,19 @@ class player : public Character int item_reload_cost( const item &it, const item &ammo, int qty ) const; /** Wear item; returns false on fail. If interactive is false, don't alert the player or drain moves on completion. */ - cata::optional::iterator> + cata::optional wear( int pos, bool interactive = true ); - cata::optional::iterator> + cata::optional wear( item &to_wear, bool interactive = true ); /** Takes off an item, returning false on fail. The taken off item is processed in the interact */ - bool takeoff( item &it, std::list *res = nullptr ); + bool takeoff( item &it, ItemList *res = nullptr ); bool takeoff( int pos ); /** So far only called by unload() from game.cpp */ bool add_or_drop_with_msg( item &it, bool unloading = false ); - bool unload( item_location loc ); + bool unload( item &it ); /** * Try to wield a contained item consuming moves proportional to weapon skill and volume. @@ -375,7 +375,7 @@ class player : public Character /** Uses a tool */ void use( int inventory_position ); /** Uses a tool at location */ - void use( item_location loc ); + void use( item &loc ); /** Uses the current wielded weapon */ void use_wielded(); @@ -392,7 +392,7 @@ class player : public Character std::pair gunmod_installation_odds( const item &gun, const item &mod ) const; /** Starts activity to install toolmod */ - void toolmod_add( item_location tool, item_location mod ); + void toolmod_add( item &tool, item &mod ); /** Note that we've read a book at least once. **/ virtual bool has_identified( const itype_id &item_id ) const = 0; @@ -414,7 +414,6 @@ class player : public Character void reset_stats() override; private: - safe_reference_anchor anchor; enum class power_mut_ui_cmd { Exit, Activate, @@ -442,8 +441,6 @@ class player : public Character /** This handles warning the player that there current activity will not give them xp */ void handle_skill_warning( const skill_id &id, bool force_warning = false ); - void on_worn_item_transform( const item &old_it, const item &new_it ); - /** Get the formatted name of the currently wielded item (if any) with current gun mode (if gun) */ std::string weapname() const; @@ -457,14 +454,14 @@ class player : public Character * @return An item that contains the removed charges, it's effectively a * copy of the item with the proper charges. */ - item reduce_charges( int position, int quantity ); + item *reduce_charges( int position, int quantity ); /** * Remove charges from a specific item (given by a pointer to it). * Otherwise identical to @ref reduce_charges(int,int) * @param it A pointer to the item, it *must* exist. * @param quantity How many charges to remove */ - item reduce_charges( item *it, int quantity ); + item *reduce_charges( item *it, int quantity ); /** * Check whether player has a bionic power armor interface. @@ -532,7 +529,7 @@ class player : public Character void make_craft( const recipe_id &id, int batch_size, const tripoint &loc = tripoint_zero ); void make_all_craft( const recipe_id &id, int batch_size, const tripoint &loc = tripoint_zero ); /** consume components and create an active, in progress craft containing them */ - item_location start_craft( craft_command &command, const tripoint &loc ); + item *start_craft( craft_command &command, const tripoint &loc ); /** * Calculate a value representing the success of the player at crafting the given recipe, * taking player skill, recipe difficulty, npc helpers, and player mutations into account. @@ -569,13 +566,13 @@ class player : public Character select_item_component( const std::vector &components, int batch, inventory &map_inv, bool can_cancel = false, const std::function &filter = return_true, bool player_inv = true ); - std::list consume_items( const comp_selection &is, int batch, - const std::function &filter = return_true ); - std::list consume_items( map &m, const comp_selection &is, int batch, - const std::function &filter = return_true, - const tripoint &origin = tripoint_zero, int radius = PICKUP_RANGE ); - std::list consume_items( const std::vector &components, int batch = 1, - const std::function &filter = return_true ); + ItemList consume_items( const comp_selection &is, int batch, + const std::function &filter = return_true ); + ItemList consume_items( map &m, const comp_selection &is, int batch, + const std::function &filter = return_true, + const tripoint &origin = tripoint_zero, int radius = PICKUP_RANGE ); + ItemList consume_items( const std::vector &components, int batch = 1, + const std::function &filter = return_true ); /** Consume tools for the next multiplier * 5% progress of the craft */ bool craft_consume_tools( item &craft, int mulitplier, bool start_craft ); void consume_tools( const comp_selection &tool, int batch ); @@ -597,7 +594,8 @@ class player : public Character weak_ptr_fast last_target; cata::optional last_target_pos; // Save favorite ammo location - item_location ammo_location; + //TODO!: OK NOW IT'S WUT THE WUT + item *ammo_location; int scent = 0; int cash = 0; int movecounter = 0; diff --git a/src/player_activity.h b/src/player_activity.h index a22e7ea3726d..a7d35dd7b857 100644 --- a/src/player_activity.h +++ b/src/player_activity.h @@ -53,7 +53,7 @@ class player_activity */ int position = 0; std::string name; - std::vector targets; + std::vector> targets; std::vector values; std::vector str_values; std::vector coords; diff --git a/src/player_hardcoded_effects.cpp b/src/player_hardcoded_effects.cpp index b98adea95e23..f489a1e98d66 100644 --- a/src/player_hardcoded_effects.cpp +++ b/src/player_hardcoded_effects.cpp @@ -559,10 +559,10 @@ void player::hardcoded_effects( effect &it ) } } else if( id == effect_evil ) { // Worn or wielded; diminished effects - bool lesserEvil = weapon.has_effect_when_wielded( AEP_EVIL ) || - weapon.has_effect_when_carried( AEP_EVIL ); + bool lesserEvil = weapon->has_effect_when_wielded( AEP_EVIL ) || + weapon->has_effect_when_carried( AEP_EVIL ); for( auto &w : worn ) { - if( w.has_effect_when_worn( AEP_EVIL ) ) { + if( w->has_effect_when_worn( AEP_EVIL ) ) { lesserEvil = true; break; } diff --git a/src/profession.cpp b/src/profession.cpp index 36468ff8df94..2fa443d0a7d7 100644 --- a/src/profession.cpp +++ b/src/profession.cpp @@ -55,7 +55,7 @@ static class json_item_substitution std::vector> bonuses; public: std::vector get_bonus_items( const std::vector &traits ) const; - std::vector get_substitution( const item &it, const std::vector &traits ) const; + std::vector get_substitution( const item &it, const std::vector &traits ) const; } item_substitutions; /** @relates string_id */ @@ -383,16 +383,17 @@ static void clear_faults( item &it ) } } -std::list profession::items( bool male, const std::vector &traits ) const +ItemList profession::items( bool male, const std::vector &traits ) const { - std::list result; + ItemList result; auto add_legacy_items = [&result]( const itypedecvec & vec ) { for( const itypedec &elem : vec ) { - item it( elem.type_id, advanced_spawn_time(), item::default_charges_tag {} ); + //TODO!: check this whole func + item *it = item::spawn( elem.type_id, advanced_spawn_time(), item::default_charges_tag {} ); if( !elem.snip_id.is_null() ) { - it.set_snippet( elem.snip_id ); + it->set_snippet( elem.snip_id ); } - it = it.in_its_container(); + it = &it->in_its_container(); result.push_back( it ); } }; @@ -400,21 +401,21 @@ std::list profession::items( bool male, const std::vector &trait add_legacy_items( legacy_starting_items ); add_legacy_items( male ? legacy_starting_items_male : legacy_starting_items_female ); - const std::vector group_both = item_group::items_from( _starting_items, - advanced_spawn_time() ); - const std::vector group_gender = item_group::items_from( male ? _starting_items_male : - _starting_items_female, advanced_spawn_time() ); + const std::vector group_both = item_group::items_from( _starting_items, + advanced_spawn_time() ); + const std::vector group_gender = item_group::items_from( male ? _starting_items_male : + _starting_items_female, advanced_spawn_time() ); result.insert( result.begin(), group_both.begin(), group_both.end() ); result.insert( result.begin(), group_gender.begin(), group_gender.end() ); std::vector bonus = item_substitutions.get_bonus_items( traits ); for( const itype_id &elem : bonus ) { if( elem != no_bonus ) { - result.push_back( item( elem, advanced_spawn_time(), item::default_charges_tag {} ) ); + result.push_back( item::spawn( elem, advanced_spawn_time(), item::default_charges_tag {} ) ); } } for( auto iter = result.begin(); iter != result.end(); ) { - const auto sub = item_substitutions.get_substitution( *iter, traits ); + const auto sub = item_substitutions.get_substitution( **iter, traits ); if( !sub.empty() ) { result.insert( result.begin(), sub.begin(), sub.end() ); iter = result.erase( iter ); @@ -422,13 +423,13 @@ std::list profession::items( bool male, const std::vector &trait ++iter; } } - for( item &it : result ) { - clear_faults( it ); - if( it.is_holster() && it.contents.num_item_stacks() == 1 ) { - clear_faults( it.contents.front() ); + for( item *&it : result ) { + clear_faults( *it ); + if( it->is_holster() && it->contents.num_item_stacks() == 1 ) { + clear_faults( it->contents.front() ); } - if( it.has_flag( "VARSIZE" ) ) { - it.set_flag( "FIT" ); + if( it->has_flag( "VARSIZE" ) ) { + it->set_flag( "FIT" ); } } @@ -439,12 +440,12 @@ std::list profession::items( bool male, const std::vector &trait // Merge charges for items that stack with each other for( auto outer = result.begin(); outer != result.end(); ++outer ) { - if( !outer->count_by_charges() ) { + if( !( *outer )->count_by_charges() ) { continue; } for( auto inner = std::next( outer ); inner != result.end(); ) { - if( outer->stacks_with( *inner ) ) { - outer->merge_charges( *inner ); + if( ( *outer )->stacks_with( **inner ) ) { + ( *outer )->merge_charges( **inner ); inner = result.erase( inner ); } else { ++inner; @@ -452,8 +453,8 @@ std::list profession::items( bool male, const std::vector &trait } } - result.sort( []( const item & first, const item & second ) { - return first.get_layer() < second.get_layer(); + std::sort( result.begin(), result.end(), []( item * first, item * second ) { + return first->get_layer() < second->get_layer(); } ); return result; } @@ -645,11 +646,11 @@ bool json_item_substitution::trait_requirements::meets_condition( const std::vec std::none_of( absent.begin(), absent.end(), pred ); } -std::vector json_item_substitution::get_substitution( const item &it, +std::vector json_item_substitution::get_substitution( const item &it, const std::vector &traits ) const { auto iter = substitutions.find( it.typeId() ); - std::vector ret; + std::vector ret; if( iter == substitutions.end() ) { for( const item *con : it.contents.all_items_top() ) { const auto sub = get_substitution( *con, traits ); @@ -673,14 +674,15 @@ std::vector json_item_substitution::get_substitution( const item &it, if( !result.count_by_charges() ) { for( int i = 0; i < new_amt; i++ ) { - ret.push_back( result.in_its_container() ); + ret.push_back( &result.in_its_container() ); } } else { result.mod_charges( -result.charges + new_amt ); while( result.charges > 0 ) { - const item pushed = result.in_its_container(); + item *pushed = &result.in_its_container(); ret.push_back( pushed ); - result.mod_charges( pushed.contents.empty() ? -pushed.charges : -pushed.contents.back().charges ); + result.mod_charges( pushed->contents.empty() ? -pushed->charges : + -pushed->contents.back().charges ); } } } diff --git a/src/profession.h b/src/profession.h index 82fb0641a993..e864514bac86 100644 --- a/src/profession.h +++ b/src/profession.h @@ -13,6 +13,7 @@ #include "string_id.h" #include "translations.h" #include "type_id.h" +#include "colony.h" template class generic_factory; @@ -98,7 +99,7 @@ class profession std::string gender_appropriate_name( bool male ) const; std::string description( bool male ) const; signed int point_cost() const; - std::list items( bool male, const std::vector &traits ) const; + ItemList items( bool male, const std::vector &traits ) const; std::vector addictions() const; vproto_id vehicle() const; std::vector pets() const; diff --git a/src/projectile.cpp b/src/projectile.cpp index 64d45cbfc401..e094769061ac 100644 --- a/src/projectile.cpp +++ b/src/projectile.cpp @@ -34,43 +34,33 @@ projectile &projectile::operator=( const projectile &other ) speed = other.speed; range = other.range; proj_effects = other.proj_effects; + //TODO!: ownermaship here set_drop( other.get_drop() ); set_custom_explosion( other.get_custom_explosion() ); return *this; } -const item &projectile::get_drop() const +item &projectile::get_drop() const { if( drop != nullptr ) { return *drop; } - - static const item null_drop; - return null_drop; -} - -void projectile::set_drop( const item &it ) -{ - if( it.is_null() ) { - unset_drop(); - } else { - drop = std::make_unique( it ); - } + return null_item_reference(); } -void projectile::set_drop( item &&it ) +void projectile::set_drop( item &it ) { if( it.is_null() ) { unset_drop(); } else { - drop = std::make_unique( std::move( it ) ); + drop = ⁢ } } void projectile::unset_drop() { - drop.reset( nullptr ); + drop = nullptr; } const explosion_data &projectile::get_custom_explosion() const diff --git a/src/projectile.h b/src/projectile.h index 73d62f701261..42b66cfbdae4 100644 --- a/src/projectile.h +++ b/src/projectile.h @@ -29,10 +29,9 @@ struct projectile { * Returns an item that should be dropped or an item for which is_null() is true * when item to drop is unset. */ - const item &get_drop() const; + item &get_drop() const; /** Copies item `it` as a drop for this projectile. */ - void set_drop( const item &it ); - void set_drop( item &&it ); + void set_drop( item &it ); void unset_drop(); const explosion_data &get_custom_explosion() const; @@ -62,7 +61,7 @@ struct projectile { private: // Actual item used (to drop contents etc.). // Null in case of bullets (they aren't "made of cartridges"). - std::unique_ptr drop; + item *drop; std::unique_ptr custom_explosion; std::set proj_effects; }; diff --git a/src/ranged.cpp b/src/ranged.cpp index 8cd7f244455f..671160578c98 100644 --- a/src/ranged.cpp +++ b/src/ranged.cpp @@ -805,7 +805,7 @@ dispersion_sources calculate_dispersion( const map &m, const player &p, const it int player::fire_gun( const tripoint &target, int shots ) { - return fire_gun( target, shots, weapon ); + return fire_gun( target, shots, *weapon ); } int player::fire_gun( const tripoint &target, const int max_shots, item &gun ) @@ -1552,7 +1552,7 @@ static int print_aim( const player &p, const catacurses::window &w, int line_num dispersion.add_range( p.recoil_vehicle() ); const double min_recoil = calculate_aim_cap( p, pos ); - const double effective_recoil = p.effective_dispersion( p.weapon.sight_dispersion() ); + const double effective_recoil = p.effective_dispersion( p.weapon->sight_dispersion() ); const double min_dispersion = std::max( min_recoil, effective_recoil ); const double steadiness_range = MAX_RECOIL - min_dispersion; // This is a relative measure of how steady the player's aim is, @@ -1621,7 +1621,7 @@ static int draw_throw_aim( const player &p, const catacurses::window &w, int lin const auto cost_fun = [&]( const aim_type & ) { return ranged::throw_cost( p, weapon ); }; - return print_ranged_chance( p, w, line_number, ctxt, weapon, get_default_aim_type(), + return print_ranged_chance( p, w, line_number, ctxt, weapon, get_default_aim_type(), dispersion_fun, cost_fun, confidence_config, range, target_size ); @@ -1743,9 +1743,9 @@ static void cycle_action( item &weap, const tripoint &pos ) weap.put_in( item( casing ).set_flag( "CASING" ) ); } else { if( cargo.empty() ) { - here.add_item_or_charges( eject, item( casing ) ); + here.add_item_or_charges( eject, *item::spawn( casing ) ); } else { - vp->vehicle().add_item( *cargo.front(), item( casing ) ); + vp->vehicle().add_item( *cargo.front(), *item::spawn( casing ) ); } sfx::play_variant_sound( "fire_gun", "brass_eject", sfx::get_heard_volume( eject ), @@ -2015,7 +2015,7 @@ double player::gun_value( const item &weap, int ammo ) const // Penalty for dodging in melee makes the gun unusable in melee // Until NPCs get proper kiting, at least - int melee_penalty = weapon.volume() / 250_ml - get_skill_level( skill_dodge ); + int melee_penalty = weapon->volume() / 250_ml - get_skill_level( skill_dodge ); if( melee_penalty <= 0 ) { // Dispersion matters less if you can just use the gun in melee total_dispersion = std::min( total_dispersion / move_cost_factor, total_dispersion ); diff --git a/src/recipe.cpp b/src/recipe.cpp index 550be7d9d6fc..f84b4110183c 100644 --- a/src/recipe.cpp +++ b/src/recipe.cpp @@ -450,73 +450,75 @@ std::string recipe::get_consistency_error() const return std::string(); } -item recipe::create_result() const +item &recipe::create_result() const { - item newit( result_, calendar::turn, item::default_charges_tag{} ); + //TODO!: check + item *newit = item::spawn( result_, calendar::turn, item::default_charges_tag{} ); if( charges ) { - newit.charges = *charges; + newit->charges = *charges; } - if( !newit.craft_has_charges() ) { - newit.charges = 0; + if( !newit->craft_has_charges() ) { + newit->charges = 0; } else if( result_mult != 1 ) { // TODO: Make it work for charge-less items (update makes amount) - newit.charges *= result_mult; + newit->charges *= result_mult; } // Show crafted items as fitting // They might end up not fitting, but it's rare - if( newit.has_flag( flag_VARSIZE ) ) { - newit.item_tags.insert( flag_FIT ); + if( newit->has_flag( flag_VARSIZE ) ) { + newit->item_tags.insert( flag_FIT ); } if( contained ) { - newit = newit.in_container( container ); + newit = &newit->in_container( container ); } - return newit; + return *newit; } -std::vector recipe::create_results( int batch ) const +std::vector recipe::create_results( int batch ) const { - std::vector items; + std::vector items; const bool by_charges = item::count_by_charges( result_ ); if( contained || !by_charges ) { // by_charges items get their charges multiplied in create_result const int num_results = by_charges ? batch : batch * result_mult; for( int i = 0; i < num_results; i++ ) { - item newit = create_result(); - items.push_back( newit ); + item &newit = create_result(); + items.push_back( &newit ); } } else { - item newit = create_result(); + item &newit = create_result(); newit.charges *= batch; - items.push_back( newit ); + items.push_back( &newit ); } return items; } -std::vector recipe::create_byproducts( int batch ) const +std::vector recipe::create_byproducts( int batch ) const { - std::vector bps; + std::vector bps; for( const auto &e : byproducts ) { - item obj( e.first, calendar::turn, item::default_charges_tag{} ); + item &obj = *item::spawn( e.first, calendar::turn, item::default_charges_tag{} ); if( obj.has_flag( "VARSIZE" ) ) { obj.set_flag( "FIT" ); } if( obj.count_by_charges() ) { obj.charges *= e.second * batch; - bps.push_back( obj ); + bps.push_back( &obj ); } else { if( !obj.craft_has_charges() ) { obj.charges = 0; } for( int i = 0; i < e.second * batch; ++i ) { - bps.push_back( obj ); + //TODO!:check + bps.push_back( i == 0 ? &obj : item::spawn( obj ) ); } } } diff --git a/src/recipe.h b/src/recipe.h index 62387ac0860c..1dccc747cc62 100644 --- a/src/recipe.h +++ b/src/recipe.h @@ -135,11 +135,11 @@ class recipe // Create an item instance as if the recipe was just finished, // Contain charges multiplier - item create_result() const; - std::vector create_results( int batch = 1 ) const; + item &create_result() const; + std::vector create_results( int batch = 1 ) const; // Create byproduct instances as if the recipe was just finished - std::vector create_byproducts( int batch = 1 ) const; + std::vector create_byproducts( int batch = 1 ) const; bool has_byproducts() const; diff --git a/src/requirements.cpp b/src/requirements.cpp index 3f8eedce408f..c4d9c41b6cd4 100644 --- a/src/requirements.cpp +++ b/src/requirements.cpp @@ -1118,7 +1118,7 @@ requirement_data requirement_data::disassembly_requirements() const } requirement_data requirement_data::continue_requirements( const std::vector - &required_comps, const std::list &remaining_comps ) + &required_comps, const ItemList &remaining_comps ) { // Create an empty requirement_data requirement_data ret; @@ -1144,7 +1144,7 @@ requirement_data requirement_data::continue_requirements( const std::vector del; craft_components.visit_items( [&comp, &qty, &del]( item * e ) { - std::list used; + ItemList used; if( e->use_charges( comp.type, qty, used, tripoint_zero ) ) { del.push_back( e ); } diff --git a/src/requirements.h b/src/requirements.h index bb8ff7ef16e5..39681d3c7e9d 100644 --- a/src/requirements.h +++ b/src/requirements.h @@ -10,6 +10,7 @@ #include "translations.h" #include "type_id.h" +#include "colony.h" class JsonArray; class JsonIn; @@ -348,7 +349,7 @@ struct requirement_data { * Returned requirement_data is for *all* batches at once. */ static requirement_data continue_requirements( const std::vector &required_comps, - const std::list &remaining_comps ); + const ItemList &remaining_comps ); /** * Merge similar quality/tool/component lists. diff --git a/src/safe_reference.cpp b/src/safe_reference.cpp index d52082f7a93e..6abed34db749 100644 --- a/src/safe_reference.cpp +++ b/src/safe_reference.cpp @@ -1,16 +1,102 @@ #include "safe_reference.h" -safe_reference_anchor::safe_reference_anchor() +template +safe_reference::id_type safe_reference::serialize() const { - impl = std::make_shared(); + if( rec == nullptr ) { + return ID_NONE; + } + if( rec->id == ID_NONE && rec->p != nullptr ) { + rec->id = generate_new_id(); + } + //If this object is to remain loaded, we don't increase the json count as we should be saving it again before we're done. + if( save_and_quit || is_unloaded() ) { + rec->json_count++; + } + return rec->id; +} + +template +void safe_reference::deserialize( safe_reference::id_type id ) +{ + fill( id ); + if( rec == nullptr ) { + return; + } + if( rec->json_count != 0 ) { + rec->json_count--; + } // else { this is indicative of save corruption } + rec->mem_count++; +} + +template +static void safe_reference::serialize_global( JsonOut &json ) +{ + json.member( "safe_references" ); + json.start_array(); + for( rbi_it &it : records_by_id ) { + //TODO!: better format + safe_reference::id_type id = it->second->id; + uint32_t count = it->second->json_count; + json.write( id ); + json.write( count ); + } + json.end_array(); +} + +template +static void safe_reference::deserialize_global( JsonIn &jsin ) +{ + jsin.start_array(); + while( !jsin.end_array() ) { + safe_reference::id_type id; + jsin.read( id ); + uint32_t count; + jsin.read( count ); + record *rec = new record( id ); + rec->json_count = count; + records_by_id.insert( {id, rec} ); + } } -safe_reference_anchor::safe_reference_anchor( const safe_reference_anchor & ) : - safe_reference_anchor() -{} -safe_reference_anchor &safe_reference_anchor::operator=( const safe_reference_anchor & ) +template +static void safe_reference::merge( T *primary, T *secondary ) { - impl = std::make_shared(); - return *this; + + rbp_it sec_search = records_by_pointer.find( secondary ); + + //The secondary doesn't have a record (i.e. there are no references to it to redirect) so there's nothing to do + if( sec_search == records_by_pointer.end() ) { + return; + } + + rbp_it pri_search = records_by_pointer.find( primary ); + + //The primary doesn't have a record but the secondary does + if( pri_search == records_by_pointer.end() ) { + //change the secondary's record to point to the primary now + record *rec = sec_search->second; + rec->target.p = primary; + records_by_pointer.erase( secondary ); + records_by_pointer.insert( {primary, rec} ); + return; + } + + //They both have a record + //Neither of these records should be a redirect as this would imply that a secondary wasn't destroyed after being merged + record *pri_rec = pri_search->second; + record *sec_rec = sec_search->second; + + //If the secondary doesn't have an ID + if( sec_rec->id == ID_NONE ) { + sec_rec->id = REDIRECTED_MASK; + sec_rec->target.redirect = pri_rec; + } + + //They both have an id + if( pri_rec->id != ID_NONE && sec_rec->id != ID_NONE ) { + //This is the worse case + } + } diff --git a/src/safe_reference.h b/src/safe_reference.h index 5e235e0af9a9..27ec2ec4daff 100644 --- a/src/safe_reference.h +++ b/src/safe_reference.h @@ -3,71 +3,360 @@ #define CATA_SRC_SAFE_REFERENCE_H /** -A pair of classes to provide safe references to objects. +* +* Safe references are easy to use but very complex internally. Please refer to proper documentation on how to use them. +* This comments covers their internal workings rather than their public interface. +* +* Note that safe references require that the objects referenced have an is_loaded method, but more generally that it's a game object in order for the persistance to work correct. +* +* Central to safe references is their records. Each record contains 2 counts, an id and a pointer. +* The first count stores the number of live in-memory references. It can be assumed to be accurate. +* The second count stores the number of in-json references. It can't be trusted due to save scumming. +* The ID will often be ID_NONE and contains two flag bits, which will be discussed later. When comparing IDs the flag bits are ignored. +* The pointer usually stores a pointer to the target itself. If the target is in memory it reliably points to them and is nullptr otherwise. +* Note that the pointer will not be made null until the end of the turn if the target is unloaded or destroyed. It's important to check these things separatly. +* In the case that the redirect ID bit is set the pointer instead points to another record. +* +* Two in-memory global (really per GO type) unordered_maps are used to manage this. One that contains object pointers -> record pointers and one that contains ids -> record pointers. +* There are also two global json structures created when saving. These store the json counts of IDs and a table of ID redirects. Both of these are cleaned when the json count for an ID hits 0. +* Objects are not given a record until a safe reference to them is first created. These records can exist in both, either or neither of the global lists during their life. +* IDs are not added to a record until either the object itself or one of its references is saved. IDs only exist in records, not in the objects themselves. +* Records are typically cleaned up when the counts indicate we can do so, however we never forget an ID once one has been assigned and will keep that record loaded for as long as the object is. +* +* The two flags stored in IDs represent destruction and redirection. They can't be trusted due to save scumming. +* Either one or neither, but not both can be set and they aren't considered part of the ID proper. +* They are still stored in the record and persistened to json when appropriate. Note that in json references and object ids never have these bits set. Only in memory and the global structures. +* References to a destroyed thing should instead use ID_NONE with the destroyed bit set, references to a redirected thing should resolve the redirection before saving. +* Destroyed objects shouldn't be saved and likewise resolve redirections before saving. +* +* Merging and redirects is the most complicated part but fortunately they're quite rare, it's only very few merges result in a redirect flag being set. +* The big thing to remember is that a redirected entry is kept around until its counts are both 0. +* However, redirected references are replaced with the thing they redirect to at every opportunity. +* This updates the counts appropriately and so the counts of a redirected reference should only ever go down. +*/ -A safe_reference_anchor is made a member of some object, and a safe_reference -to that object (or any other object with the same lifetime) can be obtained -from the safe_reference_anchor. +#include -When the safe_reference is destroyed or assigned-to, all safe_references -derived from it are invalidated. When one attempts to fetch the referenced -object from an invalidated safe_reference, it returns nullptr. +class item; +class game; +class JsonIn; +class JsonOut; -The motivating use case is to store references to items in item_locations in a -way that is safe if that item is moved or destroyed. +template class cache_reference; -*/ +//TODO!: move some of this to .cpp probably +void reset_save_ids( uint32_t prefix, bool quitting ); -#include +//TODO!: move these maybe +uint64_t save_id_prefix; +bool save_and_quit; template class safe_reference { + protected: + struct record; public: - safe_reference() = default; + using id_type = uint64_t; + + friend void reset_save_ids( uint32_t, bool ); + friend T; + friend game; + friend cache_reference; + + protected: + using rbp_type = std::unordered_map; + using rbi_type = std::unordered_map; + using rbp_it = typename rbp_type::iterator; + using rbi_it = typename rbi_type::iterator; + + constexpr static id_type ID_NONE = 0; + constexpr static id_type DESTROYED_MASK = 1 << ( 31 ); + constexpr static id_type REDIRECTED_MASK = 1 << ( 30 ); + + struct record { + record( T *p ) : id( ID_NONE ), mem_count( 0 ), json_count( 0 ) { + target.p = p; + }; + record( id_type id ) : id( id ), mem_count( 0 ), json_count( 0 ) { + target.p = nullptr; + }; + record( T *p, id_type id ) : id( id ), mem_count( 0 ), json_count( 0 ) { + target.p = p; + }; + union { + record *redirect; + T *p; + } target; + id_type id; + uint32_t mem_count; + uint32_t json_count; + }; + mutable record *rec; + + static rbp_type records_by_pointer; + static rbi_type records_by_id; + static uint32_t next_id; + + inline void fill( T * obj ) { + rbp_it search = records_by_pointer.find( obj ); + if( search != records_by_pointer.end() ) { + rec = search->second; + } else { + rec = new record( obj ); + records_by_pointer.insert( {obj, rec} ); + } + } + inline void fill( id_type id ) { + rbi_it search = records_by_id.find( id ); + if( search != records_by_id.end() ) { + rec = search->second; + } else { + //TODO!: some sort of info message, this is indicative of save corruptioin + rec = new record( id ); + records_by_id.insert( {id, rec} ); + } + } + + inline static bool id_is_destroyed( id_type id ) { + return ( id & DESTROYED_MASK ) != 0; + } + + inline static bool id_is_redirected( id_type id ) { + return ( id & REDIRECTED_MASK ) != 0; + } - T *get() const { - return impl.lock().get(); + inline void resolve_redirects() const { + while( rec != nullptr && id_is_redirected( rec->id ) ) { + if( rec->mem_count == 1 && rec->json_count == 0 ) { + record *old_rec = rec; + rec = rec->target.redirect; + delete old_rec; + } else { + rec->mem_count--; + rec = rec->target.redirect; + } + } } - explicit operator bool() const { + inline void remove() { + resolve_redirects(); + if( rec == nullptr ) { + return; + } + //Check if we're the last in-memory reference + if( rec->mem_count == 1 ) { + if( rec->id == ID_NONE ) { + //If the record doesn't have an ID it's ok to just forget it + records_by_pointer.erase( rec->target.p ); + delete rec; + } else if( rec->json_count == 0 && id_is_destroyed( rec->id ) ) { + //If there are no more references and the object is destroyed, forget it + records_by_id.erase( rec->id ); + if( rec->target.p != nullptr ) { + records_by_pointer.erase( rec->target.p ); + } + delete rec; + } else { + //We need to keep this record around, just set its mem count to 0 + rec->mem_count--; + } + } else { + //If we're not just decrease the count + rec->mem_count--; + } + } + + void register_load( T *obj, id_type id ) { + if( id == ID_NONE ) { + return; + } + rbi_it search = records_by_id.find( id ); + if( search != records_by_id.end() ) { + search->second->target.p = obj; + } else { + record *rec = new record( obj, id ); + records_by_id.insert( {id, rec} ); + records_by_pointer.insert( {obj, rec} ); + } + } + + void register_unload( T *obj ) { + rbp_it search = records_by_pointer.find( obj ); + if( search != records_by_pointer.end() ) { + search->second->target.p = nullptr; + } + } + + id_type lookup_id( T *obj ) { + rbp_it search = records_by_pointer.find( obj ); + if( search != records_by_pointer.end() ) { + if( search->second->id == ID_NONE ) { + search->second->id = generate_new_id(); + } + return search->second->id; + } + return ID_NONE; + } + + static void serialize_global( JsonOut &json ); + static void deserialize_global( JsonIn &jsin ); + + static id_type generate_new_id() { + return save_id_prefix | next_id++; + } + + public: + + safe_reference(): rec( nullptr ) {} + + safe_reference( T * obj ) { + fill( obj ); + rec->mem_count++; + } + safe_reference( T &obj ) { + fill( &obj ); + rec->mem_count++; + } + explicit safe_reference( id_type id ) { + fill( id ); + rec->mem_count++; + } + safe_reference( const safe_reference &source ) { + rec = source.rec; + if( rec ) { + rec->mem_count++; + } + } + safe_reference( safe_reference &&source ) { + rec = source.rec; + source.rec = nullptr; + } + + safe_reference &operator=( const safe_reference &source ) { + remove(); + rec = source.rec; + if( rec ) { + rec->mem_count++; + } + return *this; + } + + safe_reference &operator=( safe_reference &&source ) { + remove(); + rec = source.rec; + source.rec = nullptr; + return *this; + } + + ~safe_reference() { + remove(); + } + + inline bool is_unassigned() const { + return rec == nullptr; + } + + inline bool is_unloaded() const { + resolve_redirects(); + if( is_unassigned() ) { + return false; + } + if( is_destroyed() ) { + return false; + } + return ( rec->target.p == nullptr || !rec->target.p->is_loaded() ); + } + + inline bool is_destroyed() const { + resolve_redirects(); + if( is_unassigned() ) { + return false; + } + return ( rec->id & DESTROYED_MASK ) != 0; + } + + id_type serialize() const; + void deserialize( id_type id ); + + inline T *get() const { + resolve_redirects(); + if( !*this ) { + //TODO!: proper error + return nullptr; + } + return rec->target.p; + } + + explicit inline operator bool() const { return !!*this; } - bool operator!() const { - return impl.expired(); + inline bool operator!() const { + return is_unassigned() || is_unloaded() || is_destroyed(); } - T &operator*() const { + inline T &operator*() const { return *get(); } - T *operator->() const { + inline T *operator->() const { return get(); } - private: - friend class safe_reference_anchor; - safe_reference( const std::shared_ptr &p ) : impl( p ) {} + inline bool operator==( const safe_reference &against ) const { + resolve_redirects(); + against.resolve_redirects(); + return rec == against.rec; + } + + inline bool operator==( const T &against ) const { + if( !rec ) { + return false; + } + resolve_redirects(); + return rec->target.p == &against; + } + + inline bool operator==( const T *against ) const { + if( !rec ) { + return against == nullptr; + } + resolve_redirects(); + return rec->target.p == against; + } + + /** + * Merge the secondary object into the primary. This means all references to the secondary will now point to the primary. + * Typically you'll want to destroy the secondary shortly afterwards. + */ + static void merge( T *primary, T *secondary ); - std::weak_ptr impl; }; -class safe_reference_anchor +template<> +safe_reference::rbp_type safe_reference::records_by_pointer; +template<> +safe_reference::rbi_type safe_reference::records_by_id; +template<> +uint32_t safe_reference::next_id; + + +void reset_save_ids( uint32_t prefix, bool quitting ) +{ + save_id_prefix = prefix; + save_id_prefix <<= 32; + save_and_quit = quitting; + safe_reference::next_id = 1; +} + + +//TODO!: make these totally separate from safe_references +template +class cache_reference : public safe_reference { public: - safe_reference_anchor(); - safe_reference_anchor( const safe_reference_anchor & ); - safe_reference_anchor &operator=( const safe_reference_anchor & ); - - template - safe_reference reference_to( T *object ) { - // Using the shared_ptr aliasing constructor - return safe_reference( std::shared_ptr( impl, object ) ); - } - private: - struct empty {}; - std::shared_ptr impl; + using safe_reference::safe_reference; }; #endif // CATA_SRC_SAFE_REFERENCE_H diff --git a/src/savegame_json.cpp b/src/savegame_json.cpp index 4d31ebadedf2..66b772860a96 100644 --- a/src/savegame_json.cpp +++ b/src/savegame_json.cpp @@ -531,12 +531,12 @@ void Character::load( const JsonObject &data ) data.read( "my_bionics", *my_bionics ); for( auto &w : worn ) { - w.on_takeoff( *this ); + w->on_takeoff( *this ); } worn.clear(); data.read( "worn", worn ); for( auto &w : worn ) { - on_item_wear( w ); + on_item_wear( *w ); } if( data.has_array( "hp_cur" ) ) { @@ -572,7 +572,8 @@ void Character::load( const JsonObject &data ) inv.json_load_items( *invin ); } - weapon = item( "null", calendar::start_of_cataclysm ); + //TODO!: CHHHECK + weapon = &null_item_reference();//item( "null", calendar::start_of_cataclysm ); data.read( "weapon", weapon ); data.read( "move_mode", move_mode ); @@ -822,7 +823,7 @@ void player::store( JsonOut &json ) const json.member( "inv" ); inv.json_save_items( json ); - if( !weapon.is_null() ) { + if( !weapon->is_null() ) { json.member( "weapon", weapon ); // also saves contents } @@ -1613,7 +1614,7 @@ void npc::load( const JsonObject &data ) data.read( "misc_rules", rules ); data.read( "combat_rules", rules ); } - real_weapon = item( "null", calendar::start_of_cataclysm ); + real_weapon = &null_item_reference();//item( "null", calendar::start_of_cataclysm ); data.read( "real_weapon", real_weapon ); cbm_weapon_index = -1; data.read( "cbm_weapon_index", cbm_weapon_index ); @@ -1679,7 +1680,7 @@ void npc::store( JsonOut &json ) const json.member( "chatbin", chatbin ); json.member( "rules", rules ); - if( !real_weapon.is_null() ) { + if( !real_weapon->is_null() ) { json.member( "real_weapon", real_weapon ); // also saves contents } json.member( "cbm_weapon_index", cbm_weapon_index ); @@ -1751,7 +1752,7 @@ void inventory::json_save_items( JsonOut &json ) const json.start_array(); for( const auto &elem : items ) { for( const auto &elem_stack_iter : elem ) { - elem_stack_iter.serialize( json ); + elem_stack_iter->serialize( json ); } } json.end_array(); @@ -2138,10 +2139,11 @@ static bool migration_required( const item &i ) /** * Merge old individual items into new count-by-charges items with same id. */ -static void migrate( cata::colony &stack ) +static void migrate( std::vector &stack ) { + //TODO!: Another stack merge, check for( auto it_src = stack.begin(); it_src != stack.end(); ) { - if( !migration_required( *it_src ) ) { + if( !migration_required( **it_src ) ) { it_src++; continue; } @@ -2149,8 +2151,8 @@ static void migrate( cata::colony &stack ) it_dst++; bool merged = false; for( ; it_dst != stack.end(); it_dst++ ) { - const item &src = *it_src; - if( it_dst->merge_charges( src ) ) { + const item &src = **it_src; + if( ( *it_dst )->merge_charges( src ) ) { it_src = stack.erase( it_src ); merged = true; break; @@ -2354,13 +2356,17 @@ void item::deserialize( JsonIn &jsin ) legacy_fast_forward_time(); } if( data.has_array( "contents" ) ) { - std::list items; + std::vector items; data.read( "contents", items ); contents = item_contents( items ); } else { data.read( "contents", contents ); } + safe_reference::id_type id; + data.read( "id", id, safe_reference::ID_NONE ); + safe_reference::register_load( this, id ); + // Sealed item migration: items with "unseals_into" set should always have contents if( contents.empty() && is_non_resealable_container() ) { convert( type->container->unseals_into ); @@ -2374,6 +2380,12 @@ void item::serialize( JsonOut &json ) const if( !contents.empty() ) { json.member( "contents", contents ); } + + safe_reference::id_type id = safe_reference::lookup_id( this ); + if( id != safe_reference::ID_NONE ) { + json.member( "id", id ); + } + } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -2453,7 +2465,7 @@ void vehicle_part::deserialize( JsonIn &jsin ) data.read( "base", base ); } else { // handle legacy format which didn't include the base item - base = item( id.obj().item ); + base = item::spawn( id.obj().item ); } data.read( "mount_dx", mount.x ); @@ -2496,7 +2508,7 @@ void vehicle_part::deserialize( JsonIn &jsin ) } // with VEHICLE tag migrate fuel tanks only if amount field exists - if( base.has_flag( "VEHICLE" ) ) { + if( base->has_flag( "VEHICLE" ) ) { if( data.has_int( "amount" ) && ammo_capacity() > 0 && legacy_fuel != itype_battery ) { ammo_set( legacy_fuel, data.get_int( "amount" ) ); } @@ -2506,21 +2518,22 @@ void vehicle_part::deserialize( JsonIn &jsin ) if( ammo_capacity() > 0 ) { ammo_set( legacy_fuel, data.get_int( "amount" ) ); } - base.item_tags.insert( "VEHICLE" ); + base->item_tags.insert( "VEHICLE" ); } if( data.has_int( "hp" ) && id.obj().durability > 0 ) { // migrate legacy savegames exploiting that all base items at that time had max_damage() of 4 - base.set_damage( 4 * itype::damage_scale - 4 * itype::damage_scale * data.get_int( "hp" ) / - id.obj().durability ); + base->set_damage( 4 * itype::damage_scale - 4 * itype::damage_scale * data.get_int( "hp" ) / + id.obj().durability ); } // legacy turrets loaded ammo via a pseudo CARGO space if( is_turret() && !items.empty() ) { - const int qty = std::accumulate( items.begin(), items.end(), 0, []( int lhs, const item & rhs ) { - return lhs + rhs.charges; + const int qty = std::accumulate( items.begin(), items.end(), 0, []( int lhs, + const item * const & rhs ) { + return lhs + rhs->charges; } ); - ammo_set( items.begin()->ammo_current(), qty ); + ammo_set( ( *items.begin() )->ammo_current(), qty ); items.clear(); } } @@ -2674,8 +2687,8 @@ void vehicle::deserialize( JsonIn &jsin ) auto it = vp.part().items.begin(); auto end = vp.part().items.end(); for( ; it != end; ++it ) { - if( it->needs_processing() ) { - active_items.add( *it, vp.mount() ); + if( ( *it )->needs_processing() ) { + active_items.add( **it, vp.mount() ); } } } @@ -4012,20 +4025,20 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version ) const point p( i, j ); jsin.start_array(); while( !jsin.end_array() ) { - item tmp; + item *tmp; jsin.read( tmp ); - if( tmp.is_emissive() ) { - update_lum_add( p, tmp ); + if( tmp->is_emissive() ) { + update_lum_add( p, *tmp ); } if( savegame_loading_version >= 27 && version < 27 ) { - tmp.legacy_fast_forward_time(); + tmp->legacy_fast_forward_time(); } - const cata::colony::iterator it = itm[p.x][p.y].insert( tmp ); - if( tmp.needs_processing() ) { - active_items.add( *it, p ); + itm[p.x][p.y].push_back( tmp ); + if( tmp->needs_processing() ) { + active_items.add( *tmp, p ); } } } @@ -4153,7 +4166,7 @@ void submap::load( JsonIn &jsin, const std::string &member_name, int version ) } jsin.start_array(); while( !jsin.end_array() ) { - item tmp; + item *tmp; jsin.read( tmp ); pc.components.push_back( tmp ); } diff --git a/src/start_location.cpp b/src/start_location.cpp index e977e5da3598..491b0491976f 100644 --- a/src/start_location.cpp +++ b/src/start_location.cpp @@ -187,8 +187,8 @@ static void board_up( map &m, const tripoint_range &range ) m.furn_set( bp, m.furn( fp ) ); m.furn_set( fp, f_null ); auto destination_items = m.i_at( bp ); - for( const item &moved_item : m.i_at( fp ) ) { - destination_items.insert( moved_item ); + for( item *&moved_item : m.i_at( fp ) ) { + destination_items.insert( *moved_item ); } m.i_clear( fp ); } diff --git a/src/submap.h b/src/submap.h index 4dc00523bb80..87f19b4a6c63 100644 --- a/src/submap.h +++ b/src/submap.h @@ -52,7 +52,7 @@ struct maptile_soa { ter_id ter[sx][sy]; // Terrain on each square furn_id frn[sx][sy]; // Furniture on each square std::uint8_t lum[sx][sy]; // Number of items emitting light on each square - cata::colony itm[sx][sy]; // Items on each square + cata::colony itm[sx][sy]; // Items on each square field fld[sx][sy]; // Field on each square trap_id trp[sx][sy]; // Trap on each square int rad[sx][sy]; // Irradiation of each square @@ -147,7 +147,7 @@ class submap : maptile_soa // the count below 255. int count = 0; for( const auto &it : itm[p.x][p.y] ) { - if( it.is_emissive() ) { + if( it->is_emissive() ) { count++; } } @@ -158,11 +158,11 @@ class submap : maptile_soa } // TODO: Replace this as it essentially makes itm public - cata::colony &get_items( const point &p ) { + cata::colony &get_items( const point &p ) { return itm[p.x][p.y]; } - const cata::colony &get_items( const point &p ) const { + const cata::colony &get_items( const point &p ) const { return itm[p.x][p.y]; } @@ -336,7 +336,7 @@ struct maptile { // Assumes there is at least one item const item &get_uppermost_item() const { - return *std::prev( sm->get_items( pos() ).cend() ); + return **std::prev( sm->get_items( pos() ).cend() ); } }; diff --git a/src/suffer.cpp b/src/suffer.cpp index e218cf4cc878..34524de7ffb3 100644 --- a/src/suffer.cpp +++ b/src/suffer.cpp @@ -432,10 +432,10 @@ void Character::suffer_from_chemimbalance() void Character::suffer_from_schizophrenia() { std::string i_name_w; - if( !weapon.is_null() ) { - i_name_w = weapon.has_var( "item_label" ) ? weapon.get_var( "item_label" ) : + if( !weapon->is_null() ) { + i_name_w = weapon->has_var( "item_label" ) ? weapon->get_var( "item_label" ) : //~ %1$s: weapon name - string_format( _( "your %1$s" ), weapon.type_name() ); + string_format( _( "your %1$s" ), weapon->type_name() ); } // Start with the effects that both NPCs and avatars can suffer from // Delusions @@ -491,14 +491,14 @@ void Character::suffer_from_schizophrenia() return; } // Drop weapon - if( one_turn_in( 2_days ) && !weapon.is_null() ) { + if( one_turn_in( 2_days ) && !weapon->is_null() ) { const translation snip = SNIPPET.random_from_category( "schizo_weapon_drop" ).value_or( translation() ); std::string str = string_format( snip, i_name_w ); str[0] = toupper( str[0] ); add_msg_if_player( m_bad, "%s", str ); - item_location loc( *this, &weapon ); + item_location loc( *this, weapon ); drop( loc, pos() ); return; } @@ -569,7 +569,7 @@ void Character::suffer_from_schizophrenia() } // Talking weapon - if( !weapon.is_null() ) { + if( !weapon->is_null() ) { // If player has a weapon, picks a message from said weapon // Weapon tells player to kill a monster if any are nearby // Weapon is concerned for player if bleeding @@ -597,7 +597,7 @@ void Character::suffer_from_schizophrenia() i_talk_w = SNIPPET.random_from_category( "schizo_weapon_talk_bleeding" ).value_or( translation() ).translated(); does_talk = true; - } else if( weapon.damage() >= weapon.max_damage() / 3 && one_turn_in( 1_hours ) ) { + } else if( weapon->damage() >= weapon->max_damage() / 3 && one_turn_in( 1_hours ) ) { i_talk_w = SNIPPET.random_from_category( "schizo_weapon_talk_damaged" ).value_or( translation() ).translated(); does_talk = true; @@ -791,15 +791,15 @@ std::map Character::bodypart_exposure() bp_exposure[bp] = 1.0; } // For every item worn, for every body part, adjust coverage - for( const item &it : worn ) { + for( const item * const &it : worn ) { // What body parts does this item cover? - body_part_set covered = it.get_covered_body_parts(); + body_part_set covered = it->get_covered_body_parts(); for( const bodypart_id &bp : all_body_parts ) { if( bp->token != num_bp && !covered.test( bp->token ) ) { continue; } // How much exposure does this item leave on this part? (1.0 == naked) - float part_exposure = 1.0 - it.get_coverage() / 100.0f; + float part_exposure = 1.0 - it->get_coverage() / 100.0f; // Coverage multiplies, so two layers with 50% coverage will together give 75% bp_exposure[bp] = bp_exposure[bp] * part_exposure; } @@ -841,7 +841,7 @@ void Character::suffer_from_sunburn() } } // Umbrellas can keep the sun off the skin - if( weapon.has_flag( "RAIN_PROTECT" ) ) { + if( weapon->has_flag( "RAIN_PROTECT" ) ) { return; } @@ -1235,14 +1235,14 @@ void Character::suffer_from_bad_bionics() moves -= 150; mod_power_level( -10_kJ ); - if( weapon.typeId() == itype_e_handcuffs && weapon.charges > 0 ) { - weapon.charges -= rng( 1, 3 ) * 50; - if( weapon.charges < 1 ) { - weapon.charges = 1; + if( weapon->typeId() == itype_e_handcuffs && weapon->charges > 0 ) { + weapon->charges -= rng( 1, 3 ) * 50; + if( weapon->charges < 1 ) { + weapon->charges = 1; } add_msg_if_player( m_good, _( "The %s seems to be affected by the discharge." ), - weapon.tname() ); + weapon->tname() ); } sfx::play_variant_sound( "bionics", "elec_discharge", 100 ); } diff --git a/src/trapfunc.cpp b/src/trapfunc.cpp index f10f1751e945..3486506c61ea 100644 --- a/src/trapfunc.cpp +++ b/src/trapfunc.cpp @@ -66,8 +66,8 @@ static float pit_effectiveness( const tripoint &p ) { units::volume corpse_volume = 0_ml; for( auto &pit_content : g->m.i_at( p ) ) { - if( pit_content.is_corpse() ) { - corpse_volume += pit_content.volume(); + if( pit_content->is_corpse() ) { + corpse_volume += pit_content->volume(); } } diff --git a/src/turret.cpp b/src/turret.cpp index def4fdbcf612..a5ae0c1f1db3 100644 --- a/src/turret.cpp +++ b/src/turret.cpp @@ -34,7 +34,7 @@ std::vector vehicle::turrets() std::vector res; for( auto &e : parts ) { - if( !e.is_broken() && e.base.is_gun() ) { + if( !e.is_broken() && e.base->is_gun() ) { res.push_back( &e ); } } @@ -47,7 +47,7 @@ std::vector vehicle::turrets( const tripoint &target ) // exclude turrets not ready to fire or where target is out of range res.erase( std::remove_if( res.begin(), res.end(), [&]( const vehicle_part * e ) { return turret_query( *e ).query() != turret_data::status::ready || - rl_dist( global_part_pos3( *e ), target ) > e->base.gun_range(); + rl_dist( global_part_pos3( *e ), target ) > e->base->gun_range(); } ), res.end() ); return res; } @@ -83,12 +83,12 @@ std::string turret_data::name() const item_location turret_data::base() { - return item_location( vehicle_cursor( *veh, veh->index_of_part( part ) ), &part->base ); + return item_location( vehicle_cursor( *veh, veh->index_of_part( part ) ), part->base ); } item_location turret_data::base() const { - return item_location( vehicle_cursor( *veh, veh->index_of_part( part ) ), &part->base ); + return item_location( vehicle_cursor( *veh, veh->index_of_part( part ) ), part->base ); } int turret_data::ammo_remaining() const @@ -99,7 +99,7 @@ int turret_data::ammo_remaining() const if( part->info().has_flag( "USE_TANKS" ) ) { return veh->fuel_left( ammo_current() ); } - return part->base.ammo_remaining(); + return part->base->ammo_remaining(); } int turret_data::ammo_capacity() const @@ -107,7 +107,7 @@ int turret_data::ammo_capacity() const if( !veh || !part || part->info().has_flag( "USE_TANKS" ) ) { return 0; } - return part->base.ammo_capacity(); + return part->base->ammo_capacity(); } const itype *turret_data::ammo_data() const @@ -118,7 +118,7 @@ const itype *turret_data::ammo_data() const if( part->info().has_flag( "USE_TANKS" ) ) { return ammo_current().is_null() ? nullptr : &*ammo_current(); } - return part->base.ammo_data(); + return part->base->ammo_data(); } itype_id turret_data::ammo_current() const @@ -130,8 +130,8 @@ itype_id turret_data::ammo_current() const if( opts.count( part->info().default_ammo ) ) { return part->info().default_ammo; } - if( opts.count( part->base.ammo_default() ) ) { - return part->base.ammo_default(); + if( opts.count( part->base->ammo_default() ) ) { + return part->base->ammo_default(); } return opts.empty() ? itype_id::NULL_ID() : *opts.begin(); } @@ -145,15 +145,15 @@ std::set turret_data::ammo_options() const } if( !part->info().has_flag( "USE_TANKS" ) ) { - if( !part->base.ammo_current().is_null() ) { - opts.insert( part->base.ammo_current() ); + if( !part->base->ammo_current().is_null() ) { + opts.insert( part->base->ammo_current() ); } } else { for( const auto &e : veh->fuels_left() ) { const itype *fuel = &*e.first; - if( fuel->ammo && part->base.ammo_types().count( fuel->ammo->type ) && - e.second >= part->base.ammo_required() ) { + if( fuel->ammo && part->base->ammo_types().count( fuel->ammo->type ) && + e.second >= part->base->ammo_required() ) { opts.insert( fuel->get_id() ); } @@ -178,7 +178,7 @@ std::set turret_data::ammo_effects() const if( !veh || !part ) { return std::set(); } - auto res = part->base.ammo_effects(); + auto res = part->base->ammo_effects(); if( part->info().has_flag( "USE_TANKS" ) && ammo_data() ) { res.insert( ammo_data()->ammo->ammo_effects.begin(), ammo_data()->ammo->ammo_effects.end() ); } @@ -194,9 +194,9 @@ int turret_data::range() const if( ammo_data()->ammo->shape ) { return ammo_data()->ammo->shape->get_range(); } - return part->base.gun_range() + ammo_data()->ammo->range; + return part->base->gun_range() + ammo_data()->ammo->range; } - return part->base.gun_range(); + return part->base->gun_range(); } bool turret_data::in_range( const tripoint &target ) const @@ -214,11 +214,11 @@ bool turret_data::can_reload() const if( !veh || !part || part->info().has_flag( "USE_TANKS" ) ) { return false; } - if( !part->base.magazine_integral() ) { + if( !part->base->magazine_integral() ) { // always allow changing of magazines return true; } - return part->base.ammo_remaining() < part->base.ammo_capacity(); + return part->base->ammo_remaining() < part->base->ammo_capacity(); } bool turret_data::can_unload() const @@ -226,7 +226,7 @@ bool turret_data::can_unload() const if( !veh || !part || part->info().has_flag( "USE_TANKS" ) ) { return false; } - return part->base.ammo_remaining() || part->base.magazine_current(); + return part->base->ammo_remaining() || part->base->magazine_current(); } turret_data::status turret_data::query() const @@ -236,17 +236,17 @@ turret_data::status turret_data::query() const } if( part->info().has_flag( "USE_TANKS" ) ) { - if( veh->fuel_left( ammo_current() ) < part->base.ammo_required() ) { + if( veh->fuel_left( ammo_current() ) < part->base->ammo_required() ) { return status::no_ammo; } } else { - if( !part->base.ammo_sufficient() ) { + if( !part->base->ammo_sufficient() ) { return status::no_ammo; } } - auto ups = part->base.get_gun_ups_drain() * part->base.gun_current_mode().qty; + auto ups = part->base->get_gun_ups_drain() * part->base->gun_current_mode().qty; if( ups > veh->fuel_left( fuel_type_battery, true ) ) { return status::no_power; } @@ -484,7 +484,7 @@ void vehicle::turrets_set_mode() std::vector locations; for( auto &p : parts ) { - if( p.base.is_gun() ) { + if( p.base->is_gun() ) { turrets.push_back( &p ); locations.push_back( global_part_pos3( p ) ); } @@ -503,7 +503,7 @@ void vehicle::turrets_set_mode() for( auto &p : turrets ) { menu.addentry( -1, true, MENU_AUTOASSIGN, "%s [%s]", - p->name(), p->base.gun_current_mode().tname() ); + p->name(), p->base->gun_current_mode().tname() ); } menu.query(); @@ -512,7 +512,7 @@ void vehicle::turrets_set_mode() } sel = menu.ret; - turrets[ sel ]->base.gun_cycle_mode(); + turrets[ sel ]->base->gun_cycle_mode(); } } diff --git a/src/veh_interact.cpp b/src/veh_interact.cpp index 4abae98bf4b9..d53c34b0b27e 100644 --- a/src/veh_interact.cpp +++ b/src/veh_interact.cpp @@ -127,8 +127,8 @@ player_activity veh_interact::serialize_activity() if( pt != nullptr ) { if( pt->is_broken() ) { time = vp->install_time( g->u ); - } else if( pt->base.max_damage() > 0 ) { - time = vp->repair_time( g->u ) * pt->base.damage() / pt->base.max_damage(); + } else if( pt->base->max_damage() > 0 ) { + time = vp->repair_time( g->u ) * pt->base->damage() / pt->base->max_damage(); } } break; @@ -603,7 +603,7 @@ task_reason veh_interact::cant_do( char mode ) // siphon mode valid_target = false; for( const vpart_reference &vp : veh->get_any_parts( VPFLAG_FLUIDTANK ) ) { - if( vp.part().base.contents_made_of( LIQUID ) ) { + if( vp.part().base->contents_made_of( LIQUID ) ) { valid_target = true; break; } @@ -696,7 +696,7 @@ bool veh_interact::can_self_jack() int lvl = jack_quality( *veh ); for( const vpart_reference &vp : veh->get_avail_parts( "SELF_JACK" ) ) { - if( vp.part().base.has_quality( qual_SELF_JACK, lvl ) ) { + if( vp.part().base->has_quality( qual_SELF_JACK, lvl ) ) { return true; } } @@ -1227,9 +1227,9 @@ void veh_interact::do_repair() if( pt.is_broken() ) { ok = format_reqs( nmsg, vp.install_requirements(), vp.install_skills, vp.install_time( g->u ) ); } else { - if( !vp.repair_requirements().is_empty() && pt.base.max_damage() > 0 ) { - ok = format_reqs( nmsg, vp.repair_requirements() * pt.base.damage_level( 4 ), vp.repair_skills, - vp.repair_time( g->u ) * pt.base.damage() / pt.base.max_damage() ); + if( !vp.repair_requirements().is_empty() && pt.base->max_damage() > 0 ) { + ok = format_reqs( nmsg, vp.repair_requirements() * pt.base->damage_level( 4 ), vp.repair_skills, + vp.repair_time( g->u ) * pt.base->damage() / pt.base->max_damage() ); } else { nmsg += colorize( _( "This part cannot be repaired" ), c_light_red ); ok = false; @@ -1331,10 +1331,10 @@ void veh_interact::do_refill() auto validate = [&]( const item & obj ) { if( pt.is_tank() ) { if( obj.is_container() && !obj.contents.empty() ) { - return pt.can_reload( obj.contents.front() ); + return pt.can_reload( &obj.contents.front() ); } } else if( pt.is_fuel_store() ) { - bool can_reload = pt.can_reload( obj ); + bool can_reload = pt.can_reload( &obj ); if( obj.typeId() == fuel_type_battery && can_reload ) { msg = _( "You cannot recharge a vehicle battery with handheld batteries" ); return false; @@ -1479,7 +1479,7 @@ void veh_interact::calc_overview() auto details = []( const vehicle_part & pt, const catacurses::window & w, int y ) { if( !pt.ammo_current().is_null() ) { std::string specials; - const item &it = pt.base.contents.front(); + const item &it = pt.base->contents.front(); // a space isn't actually needed in front of the tags here, // but item::display_name tags use a space so this prevents // needing *second* translation for the same thing with a @@ -1733,7 +1733,7 @@ void veh_interact::move_overview_line( int amount ) vehicle_part *veh_interact::get_most_damaged_part() const { auto part_damage_comparison = []( const vpart_reference & a, const vpart_reference & b ) { - return !b.part().removed && b.part().base.damage() > a.part().base.damage(); + return !b.part().removed && b.part().base->damage() > a.part().base->damage(); }; const vehicle_part_range vpr = veh->get_all_parts(); auto high_damage_iterator = std::max_element( vpr.begin(), @@ -1940,7 +1940,7 @@ void veh_interact::do_siphon() title = _( "Select part to siphon:" ); auto sel = [&]( const vehicle_part & pt ) { - return( pt.is_tank() && pt.base.contents_made_of( LIQUID ) ); + return( pt.is_tank() && pt.base->contents_made_of( LIQUID ) ); }; auto act = [&]( const vehicle_part & pt ) { @@ -1950,10 +1950,11 @@ void veh_interact::do_siphon() hide_ui( true ); const item &base = pt.get_base(); const int idx = veh->find_part( base ); - item liquid( base.contents.back() ); - const int liq_charges = liquid.charges; - if( liquid_handler::handle_liquid( liquid, nullptr, 1, nullptr, veh, idx ) ) { - veh->drain( idx, liq_charges - liquid.charges ); + //TODO!: check + item *liquid = item::spawn( base.contents.back() ); + const int liq_charges = liquid->charges; + if( liquid_handler::handle_liquid( *liquid, nullptr, 1, nullptr, veh, idx ) ) { + veh->drain( idx, liq_charges - liquid->charges ); } }; @@ -2173,7 +2174,7 @@ void veh_interact::move_cursor( const point &d, int dstart_at ) for( size_t i = 0; i < parts_here.size(); i++ ) { auto &pt = veh->part( parts_here[i] ); - if( pt.base.damage() > 0 && pt.info().is_repairable() ) { + if( pt.base->damage() > 0 && pt.info().is_repairable() ) { need_repair.push_back( i ); } if( pt.info().has_flag( "WHEEL" ) ) { @@ -2864,12 +2865,12 @@ void veh_interact::count_durability() const vehicle_part_range vpr = veh->get_all_parts(); int qty = std::accumulate( vpr.begin(), vpr.end(), 0, []( int lhs, const vpart_reference & rhs ) { - return lhs + std::max( rhs.part().base.damage(), 0 ); + return lhs + std::max( rhs.part().base->damage(), 0 ); } ); int total = std::accumulate( vpr.begin(), vpr.end(), 0, []( int lhs, const vpart_reference & rhs ) { - return lhs + rhs.part().base.max_damage(); + return lhs + rhs.part().base->max_damage(); } ); int pct = total ? 100 * qty / total : 0; @@ -2919,10 +2920,10 @@ void act_vehicle_siphon( vehicle *veh ) if( tank ) { const item &base = tank.get_base(); const int idx = veh->find_part( base ); - item liquid( base.contents.back() ); - const int liq_charges = liquid.charges; - if( liquid_handler::handle_liquid( liquid, nullptr, 1, nullptr, veh, idx ) ) { - veh->drain( idx, liq_charges - liquid.charges ); + item *liquid = item::spawn( base.contents.back() ); + const int liq_charges = liquid->charges; + if( liquid_handler::handle_liquid( *liquid, nullptr, 1, nullptr, veh, idx ) ) { + veh->drain( idx, liq_charges - liquid->charges ); veh->invalidate_mass(); } } @@ -3031,22 +3032,22 @@ void veh_interact::complete_vehicle( player &p ) add_msg( m_info, _( "You don't meet the requirements to install the %s." ), vpinfo.name() ); break; } - + //TODO!: cheeeeck // consume items extracting a match for the parts base item - item base; + item *base = &null_item_reference(); for( const auto &e : reqs.get_components() ) { for( auto &obj : p.consume_items( e, 1, is_crafting_component ) ) { - if( obj.typeId() == vpinfo.item ) { + if( obj->typeId() == vpinfo.item ) { base = obj; } } } - if( base.is_null() ) { + if( base->is_null() ) { if( !p.has_trait( trait_DEBUG_HS ) ) { add_msg( m_info, _( "Could not find base part in requirements for %s." ), vpinfo.name() ); break; } else { - base = item( vpinfo.item ); + base = item::spawn( vpinfo.item ); } } @@ -3056,7 +3057,7 @@ void veh_interact::complete_vehicle( player &p ) p.invalidate_crafting_inventory(); - int partnum = !base.is_null() ? veh->install_part( d, part_id, + int partnum = !base->is_null() ? veh->install_part( d, part_id, std::move( base ) ) : -1; if( partnum < 0 ) { debugmsg( "complete_vehicle install part fails dx=%d dy=%d id=%s", d.x, d.y, part_id.c_str() ); @@ -3127,7 +3128,7 @@ void veh_interact::complete_vehicle( player &p ) struct vehicle_part &pt = veh->part( vehicle_part ); if( pt.is_tank() && src->is_container() && !src->contents.empty() ) { - pt.base.fill_with( src->contents.front() ); + pt.base->fill_with( src->contents.front() ); src->on_contents_changed(); if( pt.ammo_remaining() != pt.ammo_capacity() ) { @@ -3146,7 +3147,7 @@ void veh_interact::complete_vehicle( player &p ) } else if( pt.is_fuel_store() ) { auto qty = src->charges; - pt.base.reload( p, std::move( src ), qty ); + pt.base->reload( p, std::move( src ), qty ); //~ 1$s vehicle name, 2$s reactor name p.add_msg_if_player( m_good, _( "You refuel the %1$s's %2$s." ), veh->name, pt.name() ); @@ -3185,7 +3186,7 @@ void veh_interact::complete_vehicle( player &p ) p.invalidate_crafting_inventory(); // This will be a list of all the items which arise from this removal. - std::list resulting_items; + ItemList resulting_items; // First we get all the contents of the part vehicle_stack contents = veh->get_items( vehicle_part ); @@ -3218,7 +3219,7 @@ void veh_interact::complete_vehicle( player &p ) } if( !broken ) { - resulting_items.push_back( veh->part( vehicle_part ).properties_to_item() ); + resulting_items.push_back( &veh->part( vehicle_part ).properties_to_item() ); for( const auto &sk : vpinfo.install_skills ) { // removal is half as educational as installation p.practice( sk.first, veh_utils::calc_xp_gain( vpinfo, sk.first, p ) / 2 ); @@ -3246,8 +3247,8 @@ void veh_interact::complete_vehicle( player &p ) } // This will be part of an NPC "job" where they need to clean up the acitivty items afterwards if( p.is_npc() ) { - for( item &it : resulting_items ) { - it.set_var( "activity_var", p.name ); + for( item *&it : resulting_items ) { + it->set_var( "activity_var", p.name ); } } // Finally, put all the results somewhere (we wanted to wait until this diff --git a/src/veh_interact.h b/src/veh_interact.h index 205814d64c81..77b40a363485 100644 --- a/src/veh_interact.h +++ b/src/veh_interact.h @@ -59,7 +59,7 @@ class veh_interact veh_interact( vehicle &veh, const point &p = point_zero ); ~veh_interact(); - item_location target; + item *target; point dd = point_zero; /* starting offset for vehicle parts description display and max offset for scrolling */ diff --git a/src/veh_utils.cpp b/src/veh_utils.cpp index ece9e5e2b30a..224686c76b65 100644 --- a/src/veh_utils.cpp +++ b/src/veh_utils.cpp @@ -131,11 +131,12 @@ bool repair_part( vehicle &veh, vehicle_part &pt, Character &who_c ) } // consume items extracting any base item (which we will need if replacing broken part) - item base( vp.item ); + //TODO!: CHECK + item *base = item::spawn( vp.item ); for( const auto &e : reqs.get_components() ) { for( auto &obj : who.consume_items( who.select_item_component( e, 1, map_inv ), 1, is_crafting_component ) ) { - if( obj.typeId() == vp.item ) { + if( obj->typeId() == vp.item ) { base = obj; } } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 2b372cfbfeb1..6c2711f1d3f9 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -122,7 +122,7 @@ class RemovePartHandler virtual ~RemovePartHandler() = default; virtual void unboard( const tripoint &loc ) = 0; - virtual void add_item_or_charges( const tripoint &loc, item it, bool permit_oob ) = 0; + virtual void add_item_or_charges( const tripoint &loc, item &it, bool permit_oob ) = 0; virtual void set_transparency_cache_dirty( int z ) = 0; virtual void set_floor_cache_dirty( int z ) = 0; virtual void removed( vehicle &veh, int part ) = 0; @@ -137,8 +137,8 @@ class DefaultRemovePartHandler : public RemovePartHandler void unboard( const tripoint &loc ) override { g->m.unboard_vehicle( loc ); } - void add_item_or_charges( const tripoint &loc, item it, bool /*permit_oob*/ ) override { - g->m.add_item_or_charges( loc, std::move( it ) ); + void add_item_or_charges( const tripoint &loc, item &it, bool /*permit_oob*/ ) override { + g->m.add_item_or_charges( loc, it ); } void set_transparency_cache_dirty( const int z ) override { map &here = get_map(); @@ -193,7 +193,7 @@ class MapgenRemovePartHandler : public RemovePartHandler // Ignored. Will almost certainly not be called anyway, because // there are no creatures that could have been mounted during mapgen. } - void add_item_or_charges( const tripoint &loc, item it, bool permit_oob ) override { + void add_item_or_charges( const tripoint &loc, item &it, bool permit_oob ) override { if( !m.inbounds( loc ) ) { if( !permit_oob ) { debugmsg( "Tried to put item %s on invalid tile %s during mapgen!", @@ -202,10 +202,10 @@ class MapgenRemovePartHandler : public RemovePartHandler tripoint copy = loc; m.clip_to_bounds( copy ); assert( m.inbounds( copy ) ); // prevent infinite recursion - add_item_or_charges( copy, std::move( it ), false ); + add_item_or_charges( copy, it, false ); return; } - m.add_item_or_charges( loc, std::move( it ) ); + m.add_item_or_charges( loc, it ); } void set_transparency_cache_dirty( const int /*z*/ ) override { // Ignored for now. We don't initialize the transparency cache in mapgen anyway. @@ -232,7 +232,7 @@ vehicle_stack::iterator vehicle_stack::erase( vehicle_stack::const_iterator it ) return myorigin->remove_item( part_num, it ); } -void vehicle_stack::insert( const item &newitem ) +void vehicle_stack::insert( item &newitem ) { myorigin->add_item( part_num, newitem ); } @@ -334,7 +334,8 @@ void vehicle::add_missing_frames() } if( !found ) { // Install missing frame - parts.emplace_back( frame_id, i.mount, item( frame_id->item ) ); + //TODO!: check + parts.emplace_back( frame_id, i.mount, *item::spawn( frame_id->item ) ); } } } @@ -387,7 +388,7 @@ void vehicle::init_state( int init_veh_fuel, int init_veh_status ) { // vehicle parts excluding engines are by default turned off for( auto &pt : parts ) { - pt.enabled = pt.base.is_engine(); + pt.enabled = pt.base->is_engine(); } bool destroySeats = false; @@ -1152,7 +1153,7 @@ int vehicle::part_vpower_w( const int index, const bool at_full_hp ) const int pwr = vp.info().power; if( part_flag( index, VPFLAG_ENGINE ) ) { if( pwr == 0 ) { - pwr = vhp_to_watts( vp.base.engine_displacement() ); + pwr = vhp_to_watts( vp.base->engine_displacement() ); } if( vp.info().fuel_type == fuel_type_animal ) { monster *mon = get_pet( index ); @@ -1556,15 +1557,15 @@ int vehicle::install_part( const point &dp, const vpart_id &id, bool force ) if( !( force || can_mount( dp, id ) ) ) { return -1; } - return install_part( dp, vehicle_part( id, dp, item( id.obj().item ) ) ); + return install_part( dp, vehicle_part( id, dp, *item::spawn( id.obj().item ) ) ); } -int vehicle::install_part( const point &dp, const vpart_id &id, item &&obj, bool force ) +int vehicle::install_part( const point &dp, const vpart_id &id, item &obj, bool force ) { if( !( force || can_mount( dp, id ) ) ) { return -1; } - return install_part( dp, vehicle_part( id, dp, std::move( obj ) ) ); + return install_part( dp, vehicle_part( id, dp, obj ) ); } int vehicle::install_part( const point &dp, const vehicle_part &new_part ) @@ -1929,7 +1930,7 @@ bool vehicle::remove_part( const int p, RemovePartHandler &handler ) // so we pass true here to cause such points to be clamped to the // valid bounds without printing an error (as would normally // occur). - handler.add_item_or_charges( dest, i, true ); + handler.add_item_or_charges( dest, *i, true ); } } refresh(); @@ -2391,13 +2392,13 @@ bool vehicle::split_vehicles( const std::vector> &new_vehs ) item_location vehicle::part_base( int p ) { - return item_location( vehicle_cursor( *this, p ), &parts[ p ].base ); + return item_location( vehicle_cursor( *this, p ), parts[ p ].base ); } int vehicle::find_part( const item &it ) const { auto idx = std::find_if( parts.begin(), parts.end(), [&it]( const vehicle_part & e ) { - return &e.base == ⁢ + return e.base == ⁢ } ); return idx != parts.end() ? std::distance( parts.begin(), idx ) : INT_MIN; } @@ -3863,7 +3864,7 @@ void vehicle::noise_and_smoke( int load, time_duration time ) if( part_info( p ).has_flag( "E_COMBUSTION" ) ) { combustion = true; double health = parts[p].health_percent(); - if( parts[ p ].base.faults.count( fault_filter_fuel ) ) { + if( parts[ p ].base->faults.count( fault_filter_fuel ) ) { health = 0.0; } if( health < part_info( p ).engine_backfire_threshold() && one_in( 50 + 150 * health ) ) { @@ -3871,7 +3872,7 @@ void vehicle::noise_and_smoke( int load, time_duration time ) } double j = cur_stress * to_turns( time ) * muffle * 1000; - if( parts[ p ].base.faults.count( fault_filter_air ) ) { + if( parts[ p ].base->faults.count( fault_filter_air ) ) { bad_filter = true; j *= j; } @@ -5376,7 +5377,7 @@ int vehicle::add_charges( int part, const item &itm ) return add_item( part, itm_copy ) ? ret : 0; } -cata::optional vehicle::add_item( vehicle_part &pt, const item &obj ) +cata::optional vehicle::add_item( vehicle_part &pt, item &obj ) { int idx = index_of_part( &pt ); if( idx < 0 ) { @@ -5386,7 +5387,7 @@ cata::optional vehicle::add_item( vehicle_part &pt, con return add_item( idx, obj ); } -cata::optional vehicle::add_item( int part, const item &itm ) +cata::optional vehicle::add_item( int part, item &itm ) { if( part < 0 || part >= static_cast( parts.size() ) ) { debugmsg( "int part (%d) is out of range", part ); @@ -5399,8 +5400,8 @@ cata::optional vehicle::add_item( int part, const item return cata::nullopt; } - if( p.base.is_gun() ) { - if( !itm.is_ammo() || !p.base.ammo_types().count( itm.ammo_type() ) ) { + if( p.base->is_gun() ) { + if( !itm.is_ammo() || !p.base->ammo_types().count( itm.ammo_type() ) ) { return cata::nullopt; } } @@ -5422,25 +5423,30 @@ cata::optional vehicle::add_item( int part, const item } } - item itm_copy = itm; - if( itm_copy.is_bucket_nonempty() ) { - itm_copy.contents.spill_contents( global_part_pos3( part ) ); + if( itm.is_bucket_nonempty() ) { + itm.contents.spill_contents( global_part_pos3( part ) ); } - const vehicle_stack::iterator new_pos = p.items.insert( itm_copy ); - if( itm_copy.needs_processing() ) { - active_items.add( *new_pos, p.mount ); + p.items.push_back( &itm ); + if( itm.needs_processing() ) { + active_items.add( itm, p.mount ); } invalidate_mass(); - return cata::optional( new_pos ); + return cata::optional( std::prev( p.items.end() ) ); } bool vehicle::remove_item( int part, item *it ) { - const cata::colony &veh_items = parts[part].items; - const cata::colony::const_iterator iter = veh_items.get_iterator_from_pointer( it ); + const std::vector &veh_items = parts[part].items; + //const std::vector::const_iterator iter = veh_items.get_iterator_from_pointer( it ); + + const std::vector::const_iterator iter = std::find_if( veh_items.begin(), + veh_items.end(), [&it]( const item * const & item ) { + return it == item; + } ); + if( iter == veh_items.end() ) { return false; } @@ -5450,10 +5456,10 @@ bool vehicle::remove_item( int part, item *it ) vehicle_stack::iterator vehicle::remove_item( int part, vehicle_stack::const_iterator it ) { - cata::colony &veh_items = parts[part].items; + cata::colony &veh_items = parts[part].items; // remove from the active items cache (if it isn't there does nothing) - active_items.remove( &*it ); + active_items.remove( *it ); invalidate_mass(); return veh_items.erase( it ); @@ -5501,9 +5507,9 @@ void vehicle::place_spawn_items() continue; } - std::vector created; + std::vector created; for( const itype_id &e : spawn.item_ids ) { - created.emplace_back( item( e ).in_its_container() ); + created.emplace_back( &item::spawn( e )->in_its_container() ); } for( const item_group_id &e : spawn.item_groups ) { item_group::ItemList group_items = item_group::items_from( e, calendar::start_of_cataclysm ); @@ -5512,26 +5518,26 @@ void vehicle::place_spawn_items() } } - for( item &e : created ) { - if( e.is_null() ) { + for( item *&e : created ) { + if( e->is_null() ) { continue; } - if( broken && e.mod_damage( rng( 1, e.max_damage() ) ) ) { + if( broken && e->mod_damage( rng( 1, e->max_damage() ) ) ) { continue; // we destroyed the item } - if( e.is_tool() || e.is_gun() || e.is_magazine() ) { - bool spawn_ammo = rng( 0, 99 ) < spawn.with_ammo && e.ammo_remaining() == 0; - bool spawn_mag = rng( 0, 99 ) < spawn.with_magazine && !e.magazine_integral() && - !e.magazine_current(); + if( e->is_tool() || e->is_gun() || e->is_magazine() ) { + bool spawn_ammo = rng( 0, 99 ) < spawn.with_ammo && e->ammo_remaining() == 0; + bool spawn_mag = rng( 0, 99 ) < spawn.with_magazine && !e->magazine_integral() && + !e->magazine_current(); if( spawn_mag ) { - e.put_in( item( e.magazine_default(), e.birthday() ) ); + e->put_in( *item::spawn( e->magazine_default(), e->birthday() ) ); } if( spawn_ammo ) { - e.ammo_set( e.ammo_default() ); + e->ammo_set( e->ammo_default() ); } } - add_item( part, e ); + add_item( part, *e ); } } } @@ -5595,8 +5601,8 @@ void vehicle::gain_moves() void vehicle::dump_items_from_part( const size_t index ) { vehicle_part &vp = parts[ index ]; - for( item &e : vp.items ) { - g->m.add_item_or_charges( global_part_pos3( vp ), e ); + for( item *&e : vp.items ) { + g->m.add_item_or_charges( global_part_pos3( vp ), *e ); } vp.items.clear(); } @@ -6535,7 +6541,7 @@ int vehicle::break_off( int p, int dmg ) } const tripoint pos = global_part_pos3( p ); const auto scatter_parts = [&]( const vehicle_part & pt ) { - for( const item &piece : pt.pieces_for_broken_part() ) { + for( item *&piece : pt.pieces_for_broken_part() ) { // inside the loop, so each piece goes to a different place // TODO: this may spawn items behind a wall const tripoint where = random_entry( g->m.points_in_radius( pos, SCATTER_DISTANCE ) ); @@ -6543,7 +6549,7 @@ int vehicle::break_off( int p, int dmg ) // to build the component (smash a vehicle box that took 10 lumps of steel, // find 12 steel lumps scattered after atom-smashing it with a tree trunk) if( !magic ) { - g->m.add_item_or_charges( where, piece ); + g->m.add_item_or_charges( where, *piece ); } } }; @@ -6657,8 +6663,8 @@ int vehicle::damage_direct( int p, int dmg, damage_type type ) // destroyed parts lose any contained fuels, battery charges or ammo leak_fuel( parts [ p ] ); - for( const auto &e : parts[p].items ) { - g->m.add_item_or_charges( global_part_pos3( p ), e ); + for( auto &e : parts[p].items ) { + g->m.add_item_or_charges( global_part_pos3( p ), *e ); } parts[p].items.clear(); @@ -6706,7 +6712,7 @@ void vehicle::leak_fuel( vehicle_part &pt ) int qty = pt.ammo_consume( rng( 0, std::max( pt.ammo_remaining() / 3, 1 ) ), global_part_pos3( pt ) ); if( qty > 0 ) { - g->m.add_item_or_charges( random_entry( tiles ), item( fuel, calendar::turn, qty ) ); + g->m.add_item_or_charges( random_entry( tiles ), *item::spawn( fuel, calendar::turn, qty ) ); } } @@ -6873,8 +6879,9 @@ void vehicle::update_time( const time_point &update_to ) const weather_sum accum_weather = sum_conditions( update_from, update_to, global_square_location().raw() ); // make some reference objects to use to check for reload - const item water( "water" ); - const item water_clean( "water_clean" ); + //TODO!: fuuuck no, this needs a looking at + const item *water = item::spawn( "water" ); + const item *water_clean = item::spawn( "water_clean" ); for( int idx : funnels ) { const auto &pt = parts[idx]; @@ -6994,9 +7001,9 @@ void vehicle::calc_mass_center( bool use_precalc ) const units::mass m_part = 0_gram; units::mass m_part_items = 0_gram; - m_part += vp.part().base.weight(); + m_part += vp.part().base->weight(); for( const auto &j : get_items( i ) ) { - m_part_items += j.weight(); + m_part_items += j->weight(); } if( vp.part().info().cargo_weight_modifier != 100 ) { m_part_items *= static_cast( vp.part().info().cargo_weight_modifier ) / 100.0f; diff --git a/src/vehicle.h b/src/vehicle.h index 76dfcaebfe84..f02884e0755a 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -126,10 +126,10 @@ class vehicle_stack : public item_stack vehicle *myorigin; int part_num; public: - vehicle_stack( cata::colony *newstack, point newloc, vehicle *neworigin, int part ) : + vehicle_stack( cata::colony *newstack, point newloc, vehicle *neworigin, int part ) : item_stack( newstack ), location( newloc ), myorigin( neworigin ), part_num( part ) {} iterator erase( const_iterator it ) override; - void insert( const item &newitem ) override; + void insert( item &newitem ) override; int count_limit() const override { return MAX_ITEM_IN_VEHICLE_STORAGE; } @@ -186,7 +186,6 @@ struct vehicle_part { friend vehicle; friend class veh_interact; friend visitable; - friend item_location; friend class turret_data; enum : int { passenger_flag = 1, @@ -199,7 +198,7 @@ struct vehicle_part { vehicle_part(); /** DefaultConstructible */ - vehicle_part( const vpart_id &vp, const point &dp, item &&obj ); + vehicle_part( const vpart_id &vp, const point &dp, item &obj ); /** Check this instance is non-null (not default constructed) */ explicit operator bool() const; @@ -266,7 +265,7 @@ struct vehicle_part { double consume_energy( const itype_id &ftype, double energy_j ); /* @retun true if part in current state be reloaded optionally with specific itype_id */ - bool can_reload( const item &obj = item() ) const; + bool can_reload( const item *obj = nullptr ) const; /** * If this part is capable of wholly containing something, process the @@ -437,8 +436,8 @@ struct vehicle_part { /** As a performance optimization we cache the part information here on first lookup */ mutable const vpart_info *info_cache = nullptr; - item base; - cata::colony items; // inventory + item *base; + cata::colony items; // inventory /** Preferred ammo type when multiple are available */ itype_id ammo_pref = itype_id::NULL_ID(); @@ -457,12 +456,12 @@ struct vehicle_part { void deserialize( JsonIn &jsin ); const item &get_base() const; - void set_base( const item &new_base ); + void set_base( item &new_base ); /** * Generate the corresponding item from this vehicle part. It includes * the hp (item damage), fuel charges (battery or liquids), aspect, ... */ - item properties_to_item() const; + item &properties_to_item() const; /** * Returns an ItemList of the pieces that should arise from breaking * this part. @@ -489,8 +488,8 @@ class turret_data std::string name() const; /** Get base item location */ - item_location base(); - item_location base() const; + item &base(); + item &base() const; const vehicle *get_veh() const { return veh; @@ -897,7 +896,7 @@ class vehicle int install_part( const point &dp, const vehicle_part &part ); /** install item specified item to vehicle as a vehicle part */ - int install_part( const point &dp, const vpart_id &id, item &&obj, bool force = false ); + int install_part( const point &dp, const vpart_id &id, item &obj, bool force = false ); // find a single tile wide vehicle adjacent to a list of part indices bool try_to_rack_nearby_vehicle( const std::vector> &list_of_racks ); @@ -934,7 +933,7 @@ class vehicle bool split_vehicles( const std::vector> &new_veh ); /** Get handle for base item of part */ - item_location part_base( int p ); + item &part_base( int p ); /** Get index of part with matching base item or INT_MIN if not found */ int find_part( const item &it ) const; @@ -1502,7 +1501,7 @@ class vehicle * Update an item's active status, for example when adding * hot or perishable liquid to a container. */ - void make_active( item_location &loc ); + void make_active( item &loc ); /** * Try to add an item to part's cargo. * @@ -1510,9 +1509,9 @@ class vehicle * the volume limit or item count limit, not all charges can fit, etc.) * Otherwise, returns an iterator to the added item in the vehicle stack */ - cata::optional add_item( int part, const item &itm ); + cata::optional add_item( int part, item &itm ); /** Like the above */ - cata::optional add_item( vehicle_part &pt, const item &obj ); + cata::optional add_item( vehicle_part &pt, item &obj ); /** * Add an item counted by charges to the part's cargo. * diff --git a/src/vehicle_move.cpp b/src/vehicle_move.cpp index 88ba489cbe3a..cc3f22b67f7c 100644 --- a/src/vehicle_move.cpp +++ b/src/vehicle_move.cpp @@ -604,7 +604,7 @@ veh_collision vehicle::part_collision( int part, const tripoint &p, // because it involves iterating over all cargo // Rotors only use rotor mass in calculation. const float mass = ( part_info( part ).rotor_diameter() > 0 ) ? - to_kilogram( parts[ part ].base.weight() ) : to_kilogram( total_mass() ); + to_kilogram( parts[ part ].base->weight() ) : to_kilogram( total_mass() ); //Calculate damage resulting from d_E const material_id_list &mats = part_info( ret.part ).item->materials; diff --git a/src/vehicle_part.cpp b/src/vehicle_part.cpp index c8715d020922..a55c17b539e0 100644 --- a/src/vehicle_part.cpp +++ b/src/vehicle_part.cpp @@ -37,15 +37,15 @@ static const itype_id itype_muscle( "muscle" ); vehicle_part::vehicle_part() : id( vpart_id::NULL_ID() ) {} -vehicle_part::vehicle_part( const vpart_id &vp, const point &dp, item &&obj ) - : mount( dp ), id( vp ), base( std::move( obj ) ) +vehicle_part::vehicle_part( const vpart_id &vp, const point &dp, item &obj ) + : mount( dp ), id( vp ), base( &obj ) { // Mark base item as being installed as a vehicle part - base.set_flag( "VEHICLE" ); + base->set_flag( "VEHICLE" ); - if( base.typeId() != vp->item ) { + if( base->typeId() != vp->item ) { debugmsg( "incorrect vehicle part item, expected: %s, received: %s", - vp->item.c_str(), base.typeId().c_str() ); + vp->item.c_str(), base->typeId().c_str() ); } } @@ -56,18 +56,20 @@ vehicle_part::operator bool() const const item &vehicle_part::get_base() const { - return base; + return *base; } -void vehicle_part::set_base( const item &new_base ) +void vehicle_part::set_base( item &new_base ) { - base = new_base; + //TODO!:Destroy old + base = &new_base; } -item vehicle_part::properties_to_item() const +item &vehicle_part::properties_to_item() const { + //TODO!: the big check map &here = get_map(); - item tmp = base; + item &tmp = *item::spawn( *base ); tmp.unset_flag( "VEHICLE" ); // Cables get special handling: their target coordinates need to remain @@ -107,19 +109,19 @@ std::string vehicle_part::name( bool with_prefix ) const { auto res = info().name(); - if( base.engine_displacement() > 0 ) { - res.insert( 0, string_format( _( "%2.1fL " ), base.engine_displacement() / 100.0 ) ); + if( base->engine_displacement() > 0 ) { + res.insert( 0, string_format( _( "%2.1fL " ), base->engine_displacement() / 100.0 ) ); } else if( wheel_diameter() > 0 ) { res.insert( 0, string_format( _( "%d\" " ), wheel_diameter() ) ); } - if( base.is_faulty() ) { + if( base->is_faulty() ) { res += _( " (faulty)" ); } - if( base.has_var( "contained_name" ) ) { - res += string_format( _( " holding %s" ), base.get_var( "contained_name" ) ); + if( base->has_var( "contained_name" ) ) { + res += string_format( _( " holding %s" ), base->get_var( "contained_name" ) ); } if( is_leaking() ) { @@ -127,7 +129,7 @@ std::string vehicle_part::name( bool with_prefix ) const } if( with_prefix ) { - res.insert( 0, colorize( base.damage_symbol(), base.damage_color() ) + " " ); + res.insert( 0, colorize( base->damage_symbol(), base->damage_color() ) + " " ); } return res; } @@ -135,8 +137,8 @@ std::string vehicle_part::name( bool with_prefix ) const int vehicle_part::hp() const { const int dur = info().durability; - if( base.max_damage() > 0 ) { - return dur - dur * base.damage() / base.max_damage(); + if( base->max_damage() > 0 ) { + return dur - dur * base->damage() / base->max_damage(); } else { return dur; } @@ -144,33 +146,33 @@ int vehicle_part::hp() const int vehicle_part::damage() const { - return base.damage(); + return base->damage(); } int vehicle_part::max_damage() const { - return base.max_damage(); + return base->max_damage(); } int vehicle_part::damage_level( int max ) const { - return base.damage_level( max ); + return base->damage_level( max ); } double vehicle_part::health_percent() const { - return 1.0 - static_cast( base.damage() ) / base.max_damage(); + return 1.0 - static_cast( base->damage() ) / base->max_damage(); } double vehicle_part::damage_percent() const { - return static_cast( base.damage() ) / base.max_damage(); + return static_cast( base->damage() ) / base->max_damage(); } /** parts are considered broken at zero health */ bool vehicle_part::is_broken() const { - return base.damage() >= base.max_damage(); + return base->damage() >= base->max_damage(); } bool vehicle_part::is_unavailable( const bool carried ) const @@ -215,12 +217,12 @@ itype_id vehicle_part::ammo_current() const return itype_battery; } - if( is_tank() && !base.contents.empty() ) { - return base.contents.front().typeId(); + if( is_tank() && !base->contents.empty() ) { + return base->contents.front().typeId(); } if( is_fuel_store( false ) || is_turret() ) { - return base.ammo_current(); + return base->ammo_current(); } return itype_id::NULL_ID(); @@ -229,11 +231,11 @@ itype_id vehicle_part::ammo_current() const int vehicle_part::ammo_capacity() const { if( is_tank() ) { - return ammo_current()->charges_per_volume( base.get_container_capacity() ); + return ammo_current()->charges_per_volume( base->get_container_capacity() ); } if( is_fuel_store( false ) || is_turret() ) { - return base.ammo_capacity(); + return base->ammo_capacity(); } return 0; @@ -242,11 +244,11 @@ int vehicle_part::ammo_capacity() const int vehicle_part::ammo_remaining() const { if( is_tank() ) { - return base.contents.empty() ? 0 : base.contents.back().charges; + return base->contents.empty() ? 0 : base->contents.back().charges; } if( is_fuel_store( false ) || is_turret() ) { - return base.ammo_remaining(); + return base->ammo_remaining(); } return 0; @@ -258,20 +260,20 @@ int vehicle_part::ammo_set( const itype_id &ammo, int qty ) // We often check if ammo is set to see if tank is empty, if qty == 0 don't set ammo if( is_tank() && liquid->phase >= LIQUID && qty != 0 ) { - base.contents.clear_items(); + base->contents.clear_items(); const auto stack = units::legacy_volume_factor / std::max( liquid->stack_size, 1 ); const int limit = units::from_milliliter( ammo_capacity() ) / stack; - base.put_in( item( ammo, calendar::turn, qty > 0 ? std::min( qty, limit ) : limit ) ); + base->put_in( *item::spawn( ammo, calendar::turn, qty > 0 ? std::min( qty, limit ) : limit ) ); return qty; } if( is_turret() ) { - return base.ammo_set( ammo, qty ).ammo_remaining(); + return base->ammo_set( ammo, qty ).ammo_remaining(); } if( is_fuel_store() ) { - base.ammo_set( ammo, qty >= 0 ? qty : ammo_capacity() ); - return base.ammo_remaining(); + base->ammo_set( ammo, qty >= 0 ? qty : ammo_capacity() ); + return base->ammo_remaining(); } return -1; @@ -280,33 +282,33 @@ int vehicle_part::ammo_set( const itype_id &ammo, int qty ) void vehicle_part::ammo_unset() { if( is_tank() ) { - base.contents.clear_items(); + base->contents.clear_items(); } else if( is_fuel_store() ) { - base.ammo_unset(); + base->ammo_unset(); } } int vehicle_part::ammo_consume( int qty, const tripoint &pos ) { - if( is_tank() && !base.contents.empty() ) { + if( is_tank() && !base->contents.empty() ) { const int res = std::min( ammo_remaining(), qty ); - item &liquid = base.contents.back(); + item &liquid = base->contents.back(); liquid.charges -= res; if( liquid.charges == 0 ) { - base.contents.clear_items(); + base->contents.clear_items(); } return res; } - return base.ammo_consume( qty, pos ); + return base->ammo_consume( qty, pos ); } double vehicle_part::consume_energy( const itype_id &ftype, double energy_j ) { - if( base.contents.empty() || !is_fuel_store() ) { + if( base->contents.empty() || !is_fuel_store() ) { return 0.0f; } - item &fuel = base.contents.back(); + item &fuel = base->contents.back(); if( fuel.typeId() == ftype ) { assert( fuel.is_fuel() ); // convert energy density in MJ/L to J/ml @@ -319,7 +321,7 @@ double vehicle_part::consume_energy( const itype_id &ftype, double energy_j ) } if( charges_to_use >= fuel.charges ) { charges_to_use = fuel.charges; - base.contents.clear_items(); + base->contents.clear_items(); } else { fuel.charges -= charges_to_use; } @@ -329,25 +331,25 @@ double vehicle_part::consume_energy( const itype_id &ftype, double energy_j ) return 0.0; } -bool vehicle_part::can_reload( const item &obj ) const +bool vehicle_part::can_reload( const item *obj ) const { // first check part is not destroyed and can contain ammo if( !is_fuel_store() ) { return false; } - if( !obj.is_null() ) { - const itype_id obj_type = obj.typeId(); + if( obj != nullptr && !obj->is_null() ) { + const itype_id obj_type = obj->typeId(); if( is_reactor() ) { - return base.is_reloadable_with( obj_type ); + return base->is_reloadable_with( obj_type ); } // forbid filling tanks with solids or non-material things - if( is_tank() && ( obj.made_of( SOLID ) || obj.made_of( PNULL ) ) ) { + if( is_tank() && ( obj->made_of( SOLID ) || obj->made_of( PNULL ) ) ) { return false; } // forbid putting liquids, gasses, and plasma in things that aren't tanks - else if( !obj.made_of( SOLID ) && !is_tank() ) { + else if( !obj->made_of( SOLID ) && !is_tank() ) { return false; } // prevent mixing of different ammo @@ -359,7 +361,7 @@ bool vehicle_part::can_reload( const item &obj ) const return false; } // don't fill magazines with inappropriate fuel - if( !is_tank() && !base.is_reloadable_with( obj_type ) ) { + if( !is_tank() && !base->is_reloadable_with( obj_type ) ) { return false; } } @@ -371,33 +373,33 @@ void vehicle_part::process_contents( const tripoint &pos, const bool e_heater ) { // for now we only care about processing food containers since things like // fuel don't care about temperature yet - if( base.is_food_container() ) { + if( base->is_food_container() ) { temperature_flag flag = temperature_flag::TEMP_NORMAL; if( e_heater ) { flag = temperature_flag::TEMP_HEATER; } - base.process( nullptr, pos, false, 1, flag ); + base->process( nullptr, pos, false, 1, flag ); } } bool vehicle_part::fill_with( item &liquid, int qty ) { - if( !is_tank() || !can_reload( liquid ) ) { + if( !is_tank() || !can_reload( &liquid ) ) { return false; } - base.fill_with( liquid, qty ); + base->fill_with( liquid, qty ); return true; } const std::set &vehicle_part::faults() const { - return base.faults; + return base->faults; } std::set vehicle_part::faults_potential() const { - return base.faults_potential(); + return base->faults_potential(); } bool vehicle_part::fault_set( const fault_id &f ) @@ -405,7 +407,7 @@ bool vehicle_part::fault_set( const fault_id &f ) if( !faults_potential().count( f ) ) { return false; } - base.faults.insert( f ); + base->faults.insert( f ); return true; } @@ -417,13 +419,13 @@ int vehicle_part::wheel_area() const /** Get wheel diameter (inches) or return 0 if part is not wheel */ int vehicle_part::wheel_diameter() const { - return base.is_wheel() ? base.type->wheel->diameter : 0; + return base->is_wheel() ? base->type->wheel->diameter : 0; } /** Get wheel width (inches) or return 0 if part is not wheel */ int vehicle_part::wheel_width() const { - return base.is_wheel() ? base.type->wheel->width : 0; + return base->is_wheel() ? base->type->wheel->width : 0; } npc *vehicle_part::crew() const @@ -484,17 +486,17 @@ bool vehicle_part::is_fuel_store( bool skip_broke ) const if( skip_broke && is_broken() ) { return false; } - return is_tank() || base.is_magazine() || is_reactor(); + return is_tank() || base->is_magazine() || is_reactor(); } bool vehicle_part::is_tank() const { - return base.is_watertight_container(); + return base->is_watertight_container(); } bool vehicle_part::is_battery() const { - return base.is_magazine() && base.ammo_types().count( ammotype( "battery" ) ); + return base->is_magazine() && base->ammo_types().count( ammotype( "battery" ) ); } bool vehicle_part::is_reactor() const @@ -509,7 +511,7 @@ bool vehicle_part::is_leaking() const bool vehicle_part::is_turret() const { - return base.is_gun(); + return base->is_gun(); } bool vehicle_part::is_seat() const @@ -528,20 +530,20 @@ const vpart_info &vehicle_part::info() const void vehicle::set_hp( vehicle_part &pt, int qty ) { if( qty == pt.info().durability || pt.info().durability <= 0 ) { - pt.base.set_damage( 0 ); + pt.base->set_damage( 0 ); } else if( qty == 0 ) { - pt.base.set_damage( pt.base.max_damage() ); + pt.base->set_damage( pt.base->max_damage() ); } else { - pt.base.set_damage( pt.base.max_damage() - pt.base.max_damage() * qty / pt.info().durability ); + pt.base->set_damage( pt.base->max_damage() - pt.base->max_damage() * qty / pt.info().durability ); } } bool vehicle::mod_hp( vehicle_part &pt, int qty, damage_type dt ) { if( pt.info().durability > 0 ) { - return pt.base.mod_damage( -( pt.base.max_damage() * qty / pt.info().durability ), dt ); + return pt.base->mod_damage( -( pt.base->max_damage() * qty / pt.info().durability ), dt ); } else { return false; } diff --git a/src/vehicle_use.cpp b/src/vehicle_use.cpp index 31b7cb92b3ec..37fda9735c22 100644 --- a/src/vehicle_use.cpp +++ b/src/vehicle_use.cpp @@ -889,7 +889,8 @@ bool vehicle::fold_up() for( const vpart_reference &vp : get_any_parts( "CARGO" ) ) { const size_t p = vp.part_index(); for( auto &elem : get_items( p ) ) { - g->m.add_item_or_charges( g->u.pos(), elem ); + //TODO!: check ooowners + g->m.add_item_or_charges( g->u.pos(), *elem ); } while( !get_items( p ).empty() ) { get_items( p ).erase( get_items( p ).begin() ); @@ -1229,13 +1230,13 @@ void vehicle::reload_seeds( const tripoint &pos ) if( amount > 0 ) { int actual_amount = std::min( amount, count ); itype_id seed_id = std::get<0>( seed_entries[seed_index] ); - std::list used_seed; + ItemList used_seed; if( item::count_by_charges( seed_id ) ) { used_seed = p.use_charges( seed_id, actual_amount ); } else { used_seed = p.use_amount( seed_id, actual_amount ); } - used_seed.front().set_age( 0_turns ); + used_seed.front()->set_age( 0_turns ); //place seeds into the planter put_into_vehicle_or_drop( p, item_drop_reason::deliberate, used_seed, pos ); } @@ -1363,28 +1364,29 @@ void vehicle::operate_reaper() } // Can't use item_stack::only_item() since there might be fertilizer map_stack items = g->m.i_at( reaper_pos ); - map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item & it ) { - return it.is_seed(); + map_stack::iterator seed = std::find_if( items.begin(), items.end(), []( const item * const & it ) { + return it->is_seed(); } ); - if( seed == items.end() || seed->typeId() == itype_fungal_seeds || - seed->typeId() == itype_marloss_seed ) { + if( seed == items.end() || ( *seed )->typeId() == itype_fungal_seeds || + ( *seed )->typeId() == itype_marloss_seed ) { // Otherworldly plants, the earth-made reaper can not handle those. continue; } g->m.furn_set( reaper_pos, f_null ); // Secure the seed type before i_clear destroys the item. - const itype &seed_type = *seed->type; + const itype &seed_type = *( *seed )->type; g->m.i_clear( reaper_pos ); for( auto &i : iexamine::get_harvest_items( seed_type, plant_produced, seed_produced, false ) ) { - g->m.add_item_or_charges( reaper_pos, i ); + g->m.add_item_or_charges( reaper_pos, *i ); } sounds::sound( reaper_pos, rng( 10, 25 ), sounds::sound_t::combat, _( "Swish" ), false, "vehicle", "reaper" ); if( vp.has_feature( "CARGO" ) ) { for( map_stack::iterator iter = items.begin(); iter != items.end(); ) { - if( ( iter->volume() <= max_pickup_volume ) && - add_item( reaper_id, *iter ) ) { + //TODO!: check as shit + if( ( ( *iter )->volume() <= max_pickup_volume ) && + add_item( reaper_id, **iter ) ) { iter = items.erase( iter ); } else { ++iter; @@ -1400,7 +1402,9 @@ void vehicle::operate_planter() const size_t planter_id = vp.part_index(); const tripoint loc = vp.pos(); vehicle_stack v = get_items( planter_id ); - for( auto i = v.begin(); i != v.end(); i++ ) { + for( auto it = v.begin(); it != v.end(); it++ ) { + //TODO!: check allllla this + item *i = *it; if( i->is_seed() ) { // If it is an "advanced model" then it will avoid damaging itself or becoming damaged. It's a real feature. if( g->m.ter( loc ) != t_dirtmound && vp.has_feature( "ADVANCED_PLANTER" ) ) { @@ -1417,12 +1421,12 @@ void vehicle::operate_planter() if( !i->count_by_charges() || i->charges == 1 ) { i->set_age( 0_turns ); g->m.add_item( loc, *i ); - v.erase( i ); + v.erase( it ); } else { - item tmp = *i; - tmp.charges = 1; - tmp.set_age( 0_turns ); - g->m.add_item( loc, tmp ); + item *tmp = item::spawn( *i ); + tmp->charges = 1; + tmp->set_age( 0_turns ); + g->m.add_item( loc, *tmp ); i->charges--; } break; @@ -1459,9 +1463,9 @@ void vehicle::operate_scoop() // Ignore it. Street sweepers are not known for their ability to harvest crops. continue; } - for( item &it : items ) { - if( it.volume() < max_pickup_volume ) { - that_item_there = ⁢ + for( item *&it : items ) { + if( it->volume() < max_pickup_volume ) { + that_item_there = it; break; } } @@ -1617,12 +1621,12 @@ void vehicle::use_washing_machine( int p ) auto items = get_items( p ); static const std::string filthy( "FILTHY" ); - bool filthy_items = std::all_of( items.begin(), items.end(), []( const item & i ) { - return i.has_flag( filthy ); + bool filthy_items = std::all_of( items.begin(), items.end(), []( const item * const & i ) { + return i->has_flag( filthy ); } ); - bool cbms = std::any_of( items.begin(), items.end(), []( const item & i ) { - return i.is_bionic(); + bool cbms = std::any_of( items.begin(), items.end(), []( const item * const & i ) { + return i->is_bionic(); } ); if( parts[p].enabled ) { @@ -1674,7 +1678,7 @@ void vehicle::use_washing_machine( int p ) parts[p].enabled = true; for( auto &n : items ) { - n.set_age( 0_turns ); + n->set_age( 0_turns ); } if( fuel_left( itype_water ) >= 24 ) { @@ -1697,17 +1701,17 @@ void vehicle::use_dishwasher( int p ) bool detergent_is_enough = g->u.crafting_inventory().has_charges( itype_detergent, 5 ); auto items = get_items( p ); static const std::string filthy( "FILTHY" ); - bool filthy_items = std::all_of( items.begin(), items.end(), []( const item & i ) { - return i.has_flag( filthy ); + bool filthy_items = std::all_of( items.begin(), items.end(), []( const item * const & i ) { + return i->has_flag( filthy ); } ); std::string buffer; buffer += _( "Soft items can't be cleaned in a dishwasher, you should use a washing machine for that. You need to remove them:" ); bool soft_items = false; - for( const item &it : items ) { - if( it.is_soft() ) { + for( const item * const &it : items ) { + if( it->is_soft() ) { soft_items = true; - buffer += " " + it.tname(); + buffer += " " + it->tname(); } } @@ -1731,7 +1735,7 @@ void vehicle::use_dishwasher( int p ) } else { parts[p].enabled = true; for( auto &n : items ) { - n.set_age( 0_turns ); + n->set_age( 0_turns ); } if( fuel_left( itype_water ) >= 24 ) { diff --git a/src/visitable.cpp b/src/visitable.cpp index 27289cac9fe0..10e561962fe4 100644 --- a/src/visitable.cpp +++ b/src/visitable.cpp @@ -322,6 +322,7 @@ int visitable::max_quality( const quality_id &qual ) const /** @relates visitable */ template std::vector visitable::items_with( const std::function &filter ) +const { std::vector res; visit_items( [&res, &filter]( item * node, item * ) { @@ -333,21 +334,6 @@ std::vector visitable::items_with( const std::function -std::vector -visitable::items_with( const std::function &filter ) const -{ - std::vector res; - visit_items( [&res, &filter]( const item * node, const item * ) { - if( filter( *node ) ) { - res.push_back( node ); - } - return VisitResponse::NEXT; - } ); - return res; -} - /** @relates visitable */ template VisitResponse @@ -368,7 +354,7 @@ visitable::visit_items( const std::function &f /** @relates visitable */ template -VisitResponse visitable::visit_items( const std::function &func ) +VisitResponse visitable::visit_items( const std::function &func ) const { return visit_items( [&func]( item * it, item * ) { return func( it ); @@ -406,8 +392,8 @@ static VisitResponse visit_internal( const std::function &func, item *parent ) { - for( item &e : items ) { - switch( visit_internal( func, &e, parent ) ) { + for( item *&e : items ) { + switch( visit_internal( func, e, parent ) ) { case VisitResponse::ABORT: return VisitResponse::ABORT; default: @@ -420,7 +406,7 @@ VisitResponse item_contents::visit_contents( const std::function VisitResponse visitable::visit_items( - const std::function &func ) + const std::function &func ) const { auto it = static_cast( this ); return visit_internal( func, it ); @@ -429,12 +415,12 @@ VisitResponse visitable::visit_items( /** @relates visitable */ template <> VisitResponse visitable::visit_items( - const std::function &func ) + const std::function &func ) const { auto inv = static_cast( this ); for( auto &stack : inv->items ) { for( auto &it : stack ) { - if( visit_internal( func, &it ) == VisitResponse::ABORT ) { + if( visit_internal( func, it ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } } @@ -445,17 +431,17 @@ VisitResponse visitable::visit_items( /** @relates visitable */ template <> VisitResponse visitable::visit_items( - const std::function &func ) + const std::function &func ) const { auto ch = static_cast( this ); - if( !ch->weapon.is_null() && - visit_internal( func, &ch->weapon ) == VisitResponse::ABORT ) { + if( !ch->weapon->is_null() && + visit_internal( func, ch->weapon ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } for( auto &e : ch->worn ) { - if( visit_internal( func, &e ) == VisitResponse::ABORT ) { + if( visit_internal( func, e ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } } @@ -466,7 +452,7 @@ VisitResponse visitable::visit_items( /** @relates visitable */ template <> VisitResponse visitable::visit_items( - const std::function &func ) + const std::function &func ) const { auto cur = static_cast( this ); map &here = get_map(); @@ -475,8 +461,8 @@ VisitResponse visitable::visit_items( return VisitResponse::NEXT; } - for( item &e : here.i_at( *cur ) ) { - if( visit_internal( func, &e ) == VisitResponse::ABORT ) { + for( item *&e : here.i_at( *cur ) ) { + if( visit_internal( func, e ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } } @@ -486,7 +472,7 @@ VisitResponse visitable::visit_items( /** @relates visitable */ template <> VisitResponse visitable::visit_items( - const std::function &func ) + const std::function &func ) const { for( auto &cursor : static_cast( *this ) ) { if( cursor.visit_items( func ) == VisitResponse::ABORT ) { @@ -499,14 +485,14 @@ VisitResponse visitable::visit_items( /** @relates visitable */ template <> VisitResponse visitable::visit_items( - const std::function &func ) + const std::function &func ) const { auto self = static_cast( this ); int idx = self->veh.part_with_feature( self->part, "CARGO", true ); if( idx >= 0 ) { - for( auto &e : self->veh.get_items( idx ) ) { - if( visit_internal( func, &e ) == VisitResponse::ABORT ) { + for( auto *&e : self->veh.get_items( idx ) ) { + if( visit_internal( func, e ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } } @@ -517,7 +503,7 @@ VisitResponse visitable::visit_items( /** @relates visitable */ template <> VisitResponse visitable::visit_items( - const std::function &func ) + const std::function &func ) const { for( auto &cursor : static_cast( *this ) ) { if( cursor.visit_items( func ) == VisitResponse::ABORT ) { @@ -530,26 +516,26 @@ VisitResponse visitable::visit_items( /** @relates visitable */ template <> VisitResponse visitable::visit_items( - const std::function &func ) + const std::function &func ) const { monster *mon = static_cast( this ); - for( item &it : mon->inv ) { - if( visit_internal( func, &it ) == VisitResponse::ABORT ) { + for( item *&it : mon->inv ) { + if( visit_internal( func, it ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } } - if( mon->storage_item && visit_internal( func, &*mon->storage_item ) == VisitResponse::ABORT ) { + if( mon->storage_item && visit_internal( func, *mon->storage_item ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } - if( mon->armor_item && visit_internal( func, &*mon->armor_item ) == VisitResponse::ABORT ) { + if( mon->armor_item && visit_internal( func, *mon->armor_item ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } - if( mon->tack_item && visit_internal( func, &*mon->tack_item ) == VisitResponse::ABORT ) { + if( mon->tack_item && visit_internal( func, *mon->tack_item ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } - if( mon->tied_item && visit_internal( func, &*mon->tied_item ) == VisitResponse::ABORT ) { + if( mon->tied_item && visit_internal( func, *mon->tied_item ) == VisitResponse::ABORT ) { return VisitResponse::ABORT; } @@ -575,7 +561,7 @@ item visitable::remove_item( item &it ) } bool item_contents::remove_internal( const std::function &filter, - int &count, std::list &res ) + int &count, ItemList &res ) { for( auto it = items.begin(); it != items.end(); ) { if( filter( *it ) ) { @@ -593,11 +579,11 @@ bool item_contents::remove_internal( const std::function &filter /** @relates visitable */ template <> -std::list visitable::remove_items_with( const std::function +ItemList visitable::remove_items_with( const std::function &filter, int count ) { item *it = static_cast( this ); - std::list res; + ItemList res; if( count <= 0 ) { // nothing to do @@ -610,11 +596,11 @@ std::list visitable::remove_items_with( const std::function -std::list visitable::remove_items_with( const +ItemList visitable::remove_items_with( const std::function &filter, int count ) { auto inv = static_cast( this ); - std::list res; + ItemList res; if( count <= 0 ) { // nothing to do @@ -622,7 +608,7 @@ std::list visitable::remove_items_with( const } for( auto stack = inv->items.begin(); stack != inv->items.end() && count > 0; ) { - std::list &istack = *stack; + ItemList &istack = *stack; const auto original_invlet = istack.front().invlet; for( auto istack_iter = istack.begin(); istack_iter != istack.end() && count > 0; ) { @@ -659,11 +645,11 @@ std::list visitable::remove_items_with( const /** @relates visitable */ template <> -std::list visitable::remove_items_with( const +ItemList visitable::remove_items_with( const std::function &filter, int count ) { auto ch = static_cast( this ); - std::list res; + ItemList res; if( count <= 0 ) { // nothing to do @@ -707,11 +693,11 @@ std::list visitable::remove_items_with( const /** @relates visitable */ template <> -std::list visitable::remove_items_with( const +ItemList visitable::remove_items_with( const std::function &filter, int count ) { auto cur = static_cast( this ); - std::list res; + ItemList res; if( count <= 0 ) { // nothing to do @@ -727,7 +713,7 @@ std::list visitable::remove_items_with( const // fetch the appropriate item stack point offset; submap *sub = here.get_submap_at( *cur, offset ); - cata::colony &stack = sub->get_items( offset ); + std::vector &stack = sub->get_items( offset ); for( auto iter = stack.begin(); iter != stack.end(); ) { if( filter( *iter ) ) { @@ -758,13 +744,13 @@ std::list visitable::remove_items_with( const /** @relates visitable */ template <> -std::list visitable::remove_items_with( const +ItemList visitable::remove_items_with( const std::function &filter, int count ) { - std::list res; + ItemList res; for( auto &cursor : static_cast( *this ) ) { - std::list out = cursor.remove_items_with( filter, count ); + std::list out = cursor.remove_items_with( filter, count ); count -= out.size(); res.splice( res.end(), out ); } @@ -774,11 +760,11 @@ std::list visitable::remove_items_with( const /** @relates visitable */ template <> -std::list visitable::remove_items_with( const +ItemList visitable::remove_items_with( const std::function &filter, int count ) { auto cur = static_cast( this ); - std::list res; + ItemList res; if( count <= 0 ) { // nothing to do @@ -821,13 +807,13 @@ std::list visitable::remove_items_with( const /** @relates visitable */ template <> -std::list visitable::remove_items_with( const +ItemList visitable::remove_items_with( const std::function &filter, int count ) { - std::list res; + ItemList res; for( auto &cursor : static_cast( *this ) ) { - std::list out = cursor.remove_items_with( filter, count ); + std::list out = cursor.remove_items_with( filter, count ); count -= out.size(); res.splice( res.end(), out ); } @@ -838,7 +824,7 @@ std::list visitable::remove_items_with( const static void remove_from_item_valptr( cata::value_ptr &ptr, const std::function &filter, - int &count, std::list &res ) + int &count, std::list &res ) { if( ptr ) { if( filter( *ptr ) ) { @@ -853,10 +839,10 @@ static void remove_from_item_valptr( /** @relates visitable */ template <> -std::list visitable::remove_items_with( const +ItemList visitable::remove_items_with( const std::function &filter, int count ) { - std::list res; + ItemList res; monster *mon = static_cast( this ); diff --git a/src/visitable.h b/src/visitable.h index 11ad1d0c025e..7b04092808d3 100644 --- a/src/visitable.h +++ b/src/visitable.h @@ -10,6 +10,7 @@ #include "filter_utils.h" #include "type_id.h" +#include "colony.h" class item; @@ -34,13 +35,10 @@ class visitable * * @return This method itself only ever returns VisitResponse::Next or VisitResponse::Abort. */ - VisitResponse visit_items( const std::function &func ); - VisitResponse visit_items( const std::function &func ) - const; + VisitResponse visit_items( const std::function &func ) const; /** Lightweight version which provides only the current node */ - VisitResponse visit_items( const std::function &func ); - VisitResponse visit_items( const std::function &func ) const; + VisitResponse visit_items( const std::function &func ) const; /** * Determine the immediate parent container (if any) for an item. @@ -97,8 +95,7 @@ class visitable const std::function &filter = return_true ) const; /** Returns all items (including those within a container) matching the filter */ - std::vector items_with( const std::function &filter ); - std::vector items_with( const std::function &filter ) const; + std::vector items_with( const std::function &filter ) const; /** * Removes items contained by this instance which match the filter @@ -107,11 +104,11 @@ class visitable * @param count maximum number of items to if unspecified unlimited. A count of zero is a no-op * @return any items removed (items counted by charges are not guaranteed to be stacked) */ - std::list remove_items_with( const std::function &filter, - int count = INT_MAX ); + ItemList remove_items_with( const std::function &filter, + int count = INT_MAX ); /** Removes and returns the item which must be contained by this instance */ - item remove_item( item &it ); + item &remove_item( item &it ); }; #endif // CATA_SRC_VISITABLE_H