Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Support item_locations on NPCs #35538

Merged
merged 2 commits into from
Nov 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/activity_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2909,6 +2909,9 @@ void activity_handlers::read_do_turn( player_activity *act, player *p )

void activity_handlers::read_finish( player_activity *act, player *p )
{
if( !act->targets.front() ) {
debugmsg( "Lost target of ACT_READ" );
}
if( p->is_npc() ) {
npc *guy = dynamic_cast<npc *>( p );
guy->finish_read( * act->targets.front().get_item() );
Expand Down
1 change: 1 addition & 0 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4663,6 +4663,7 @@ T *game::critter_by_id( const character_id id )
}

// monsters don't have ids
template Character *game::critter_by_id<Character>( character_id );
template player *game::critter_by_id<player>( character_id );
template npc *game::critter_by_id<npc>( character_id );
template Creature *game::critter_by_id<Creature>( character_id );
Expand Down
85 changes: 62 additions & 23 deletions src/item_location.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "avatar.h"
#include "character.h"
#include "character_id.h"
#include "debug.h"
#include "game.h"
#include "game_constants.h"
Expand Down Expand Up @@ -84,7 +85,7 @@ class item_location::impl
return what.get();
}

bool valid() const {
virtual bool valid() const {
ensure_unpacked();
return !!what;
}
Expand Down Expand Up @@ -228,50 +229,77 @@ class item_location::impl::item_on_map : public item_location::impl
class item_location::impl::item_on_person : public item_location::impl
{
private:
Character &who;
character_id who_id;
mutable Character *who;

bool ensure_who_unpacked() const {
if( !who ) {
who = g->critter_by_id<Character>( who_id );
if( !who ) {
// If we failed to find it throw a debug message cause we're probably going to crash soon
debugmsg( "Failed to find item_location owner with character_id %d", who_id.get_value() );
return false;
}
}
return true;
}

public:
item_on_person( Character &who, item *which ) : impl( which ), who( who ) {}
item_on_person( Character &who, int idx ) : impl( idx ), who( who ) {}
item_on_person( Character &who, item *which ) : impl( which ) {
who_id = who.getID();
this->who = &who;
}

item_on_person( character_id who_id, int idx ) : impl( idx ), who_id( who_id ), who( nullptr ) {}

void serialize( JsonOut &js ) const override {
if( !ensure_who_unpacked() ) {
return;
}
js.start_object();
js.member( "type", "character" );
js.member( "idx", find_index( who, target() ) );
js.member( "character", who_id );
js.member( "idx", find_index( *who, target() ) );
js.end_object();
}

item *unpack( int idx ) const override {
return retrieve_index( who, idx );
if( !ensure_who_unpacked() ) {
return nullptr;
}
return retrieve_index( *who, idx );
}

type where() const override {
return type::character;
}

tripoint position() const override {
return who.pos();
if( !ensure_who_unpacked() ) {
return tripoint_zero;
}
return who->pos();
}

std::string describe( const Character *ch ) const override {
if( !target() ) {
if( !target() || !ensure_who_unpacked() ) {
return std::string();
}

if( ch == &who ) {
auto parents = who.parents( *target() );
if( !parents.empty() && who.is_worn( *parents.back() ) ) {
if( ch == who ) {
auto parents = who->parents( *target() );
if( !parents.empty() && who->is_worn( *parents.back() ) ) {
return parents.back()->type_name();

} else if( who.is_worn( *target() ) ) {
} else if( who->is_worn( *target() ) ) {
return _( "worn" );

} else {
return _( "inventory" );
}

} else {
return who.name;
return who->name;
}
}

Expand All @@ -294,7 +322,7 @@ class item_location::impl::item_on_person : public item_location::impl
}

int obtain_cost( const Character &ch, int qty ) const override {
if( !target() ) {
if( !target() || !ensure_who_unpacked() ) {
return 0;
}

Expand All @@ -306,41 +334,50 @@ class item_location::impl::item_on_person : public item_location::impl
obj = *target();
}

auto parents = who.parents( *target() );
if( !parents.empty() && who.is_worn( *parents.back() ) ) {
auto parents = who->parents( *target() );
if( !parents.empty() && who->is_worn( *parents.back() ) ) {
// if outermost parent item is worn status effects (e.g. GRABBED) are not applied
// holsters may also adjust the volume cost factor

if( parents.back()->can_holster( obj, true ) ) {
auto ptr = dynamic_cast<const holster_actor *>
( parents.back()->type->get_use( "holster" )->get_actor_ptr() );
mv += dynamic_cast<player &>( who ).item_handling_cost( obj, false, ptr->draw_cost );
mv += dynamic_cast<player *>( who )->item_handling_cost( obj, false, ptr->draw_cost );

} else if( parents.back()->is_bandolier() ) {
auto ptr = dynamic_cast<const bandolier_actor *>
( parents.back()->type->get_use( "bandolier" )->get_actor_ptr() );
mv += dynamic_cast<player &>( who ).item_handling_cost( obj, false, ptr->draw_cost );
mv += dynamic_cast<player *>( who )->item_handling_cost( obj, false, ptr->draw_cost );

} else {
mv += dynamic_cast<player &>( who ).item_handling_cost( obj, false,
mv += dynamic_cast<player *>( who )->item_handling_cost( obj, false,
INVENTORY_HANDLING_PENALTY / 2 );
}

} else {
// it is more expensive to obtain items from the inventory
// TODO: calculate cost for searching in inventory proportional to item volume
mv += dynamic_cast<player &>( who ).item_handling_cost( obj, true, INVENTORY_HANDLING_PENALTY );
mv += dynamic_cast<player *>( who )->item_handling_cost( obj, true, INVENTORY_HANDLING_PENALTY );
}

if( &ch != &who ) {
if( &ch != who ) {
// TODO: implement movement cost for transferring item between characters
}

return mv;
}

void remove_item() override {
who.remove_item( *what );
if( !ensure_who_unpacked() ) {
return;
}
who->remove_item( *what );
}

bool valid() const override {
ensure_who_unpacked();
ensure_unpacked();
return !!what && !!who;
}
};

Expand Down Expand Up @@ -503,7 +540,9 @@ void item_location::deserialize( JsonIn &js )
obj.read( "pos", pos );

if( type == "character" ) {
ptr.reset( new impl::item_on_person( g->u, idx ) );
character_id who_id;
obj.read( "character", who_id );
ptr.reset( new impl::item_on_person( who_id, idx ) );

} else if( type == "map" ) {
ptr.reset( new impl::item_on_map( pos, idx ) );
Expand Down