forked from CleverRaven/Cataclysm-DDA
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnpctrade_utils.cpp
154 lines (135 loc) · 5.1 KB
/
npctrade_utils.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include "npctrade_utils.h"
#include <list>
#include <map>
#include "calendar.h"
#include "clzones.h"
#include "npc.h"
#include "npc_class.h"
#include "rng.h"
#include "vehicle.h"
#include "vpart_position.h"
static const zone_type_id zone_type_LOOT_UNSORTED( "LOOT_UNSORTED" );
namespace
{
using consume_cache = std::map<itype_id, int>;
using consume_queue = std::vector<item_location>;
using dest_t = std::vector<tripoint_abs_ms>;
void _consume_item( item_location elem, consume_queue &consumed, consume_cache &cache, npc &guy,
time_duration const &elapsed )
{
if( !elem->is_owned_by( guy ) ) {
return;
}
std::list<item *> const contents = elem->all_items_top( item_pocket::pocket_type::CONTAINER );
if( contents.empty() ) {
auto it = cache.find( elem->typeId() );
if( it == cache.end() ) {
int const rate = guy.myclass->get_shopkeeper_cons_rates().get_rate( *elem, guy );
int const rate_init = rate >= 0 ? rate * to_days<int>( elapsed ) : -1;
it = cache.emplace( elem->typeId(), rate_init ).first;
}
if( it->second != 0 ) {
consumed.push_back( elem );
it->second--;
}
} else {
for( item *it : contents ) {
_consume_item( item_location( elem, it ), consumed, cache, guy, elapsed );
}
}
}
dest_t _get_shuffled_point_set( std::unordered_set<tripoint_abs_ms> const &set )
{
dest_t ret;
std::copy( set.begin(), set.end(), std::back_inserter( ret ) );
std::shuffle( ret.begin(), ret.end(), rng_get_engine() );
return ret;
}
// returns true if item wasn't placed
bool _to_map( item const &it, map &here, tripoint const &dpoint_here )
{
if( here.can_put_items_ter_furn( dpoint_here ) &&
here.free_volume( dpoint_here ) >= it.volume() ) {
item const &ret = here.add_item_or_charges( dpoint_here, it, false );
return ret.is_null();
}
return true;
}
bool _to_veh( item const &it, cata::optional<vpart_reference> const vp )
{
int const part = static_cast<int>( vp->part_index() );
if( vp->vehicle().free_volume( part ) >= it.volume() ) {
cata::optional<vehicle_stack::iterator> const ret = vp->vehicle().add_item( part, it );
return !ret.has_value();
}
return true;
}
} // namespace
void add_fallback_zone( npc &guy )
{
zone_manager &zmgr = zone_manager::get_manager();
tripoint_abs_ms const loc = guy.get_location();
faction_id const &fac_id = guy.get_fac_id();
if( !zmgr.has_near( zone_type_LOOT_UNSORTED, loc, PICKUP_RANGE, fac_id ) ) {
zmgr.add( fallback_name, zone_type_LOOT_UNSORTED, fac_id, false,
true, loc.raw() + tripoint_north_west, loc.raw() + tripoint_south_east );
DebugLog( DebugLevel::D_WARNING, DebugClass::D_GAME )
<< "Added a fallack loot zone for NPC trader " << guy.name;
}
}
std::list<item> distribute_items_to_npc_zones( std::list<item> &items, npc &guy )
{
zone_manager &zmgr = zone_manager::get_manager();
map &here = get_map();
tripoint_abs_ms const loc_abs = guy.get_location();
faction_id const &fac_id = guy.get_fac_id();
std::list<item> leftovers;
dest_t const fallback = _get_shuffled_point_set(
zmgr.get_near( zone_type_LOOT_UNSORTED, loc_abs, PICKUP_RANGE, nullptr, fac_id ) );
for( item const &it : items ) {
zone_type_id const zid =
zmgr.get_near_zone_type_for_item( it, loc_abs, PICKUP_RANGE, fac_id );
dest_t dest = zid.is_valid() ? _get_shuffled_point_set( zmgr.get_near(
zid, loc_abs, PICKUP_RANGE, &it, fac_id ) )
: dest_t();
std::copy( fallback.begin(), fallback.end(), std::back_inserter( dest ) );
bool leftover = true;
for( tripoint_abs_ms const &dpoint : dest ) {
tripoint const dpoint_here = here.getlocal( dpoint );
cata::optional<vpart_reference> const vp =
here.veh_at( dpoint_here ).part_with_feature( "CARGO", false );
if( vp && vp->vehicle().get_owner() == fac_id ) {
leftover = _to_veh( it, vp );
} else {
leftover = _to_map( it, here, dpoint_here );
}
if( !leftover ) {
break;
}
}
if( leftover ) {
leftovers.emplace_back( it );
}
}
return leftovers;
}
void consume_items_in_zones( npc &guy, time_duration const &elapsed )
{
std::unordered_set<tripoint> const src = zone_manager::get_manager().get_point_set_loot(
guy.get_location(), PICKUP_RANGE, guy.get_fac_id() );
consume_cache cache;
map &here = get_map();
for( tripoint const &pt : src ) {
consume_queue consumed;
std::list<item_location> stack =
here.items_with( pt, [&guy]( item const & it ) {
return it.is_owned_by( guy );
} );
for( item_location &elem : stack ) {
_consume_item( elem, consumed, cache, guy, elapsed );
}
for( item_location &it : consumed ) {
it.remove_item();
}
}
}