forked from cataclysmbnteam/Cataclysm-BN
-
Notifications
You must be signed in to change notification settings - Fork 0
/
scent_map.cpp
294 lines (256 loc) · 9.17 KB
/
scent_map.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
#include "scent_map.h"
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include "assign.h"
#include "calendar.h"
#include "color.h"
#include "cuboid_rectangle.h"
#include "cursesdef.h"
#include "debug.h"
#include "game.h"
#include "generic_factory.h"
#include "map.h"
#include "output.h"
#include "string_id.h"
static constexpr int SCENT_RADIUS = 40;
static nc_color sev( const size_t level )
{
static const std::array<nc_color, 22> colors = { {
c_cyan,
c_light_cyan,
c_light_blue,
c_blue,
c_light_green,
c_green,
c_yellow,
c_pink,
c_light_red,
c_red,
c_magenta,
c_brown,
c_cyan_red,
c_light_cyan_red,
c_light_blue_red,
c_blue_red,
c_light_green_red,
c_green_red,
c_yellow_red,
c_pink_red,
c_magenta_red,
c_brown_red,
}
};
return level < colors.size() ? colors[level] : c_dark_gray;
}
void scent_map::reset()
{
for( auto &elem : grscent ) {
for( auto &val : elem ) {
val = 0;
}
}
typescent = scenttype_id();
}
void scent_map::decay()
{
for( auto &elem : grscent ) {
for( auto &val : elem ) {
val = std::max( 0, val - 1 );
}
}
}
void scent_map::draw( const catacurses::window &win, const int div, const tripoint ¢er ) const
{
assert( div != 0 );
const point max( getmaxx( win ), getmaxy( win ) );
for( int x = 0; x < max.x; ++x ) {
for( int y = 0; y < max.y; ++y ) {
const int sn = get( center + point( -max.x / 2 + x, -max.y / 2 + y ) ) / div;
mvwprintz( win, point( x, y ), sev( sn / 10 ), "%d", sn % 10 );
}
}
}
void scent_map::shift( point sm_shift )
{
scent_array<int> new_scent;
for( size_t x = 0; x < MAPSIZE_X; ++x ) {
for( size_t y = 0; y < MAPSIZE_Y; ++y ) {
const point p = point( x, y ) + sm_shift;
new_scent[x][y] = inbounds( p ) ? grscent[ p.x ][ p.y ] : 0;
}
}
grscent = new_scent;
}
int scent_map::get( const tripoint &p ) const
{
if( inbounds( p ) && grscent[p.x][p.y] > 0 ) {
return get_unsafe( p );
}
return 0;
}
void scent_map::set( const tripoint &p, int value, const scenttype_id &type )
{
if( inbounds( p ) ) {
set_unsafe( p, value, type );
}
}
void scent_map::set_unsafe( const tripoint &p, int value, const scenttype_id &type )
{
grscent[p.x][p.y] = value;
if( !type.is_empty() ) {
typescent = type;
}
}
int scent_map::get_unsafe( const tripoint &p ) const
{
return grscent[p.x][p.y] - std::abs( gm.get_levz() - p.z );
}
scenttype_id scent_map::get_type( const tripoint &p ) const
{
scenttype_id id;
if( inbounds( p ) && grscent[p.x][p.y] > 0 ) {
id = typescent;
}
return id;
}
bool scent_map::inbounds( const tripoint &p ) const
{
// HACK: This weird long check here is a hack around the fact that scentmap is 2D
// A z-level can access scentmap if it is within SCENT_MAP_Z_REACH flying z-level move from player's z-level
// That is, if a flying critter could move directly up or down (or stand still) and be on same z-level as player
const int levz = gm.get_levz();
const bool scent_map_z_level_inbounds = ( p.z == levz ) ||
( std::abs( p.z - levz ) == SCENT_MAP_Z_REACH &&
get_map().valid_move( p, tripoint( p.xy(), levz ), false, true ) );
if( !scent_map_z_level_inbounds ) {
return false;
}
static constexpr point scent_map_boundary_min{};
static constexpr point scent_map_boundary_max( MAPSIZE_X, MAPSIZE_Y );
static constexpr half_open_rectangle<point> scent_map_boundaries(
scent_map_boundary_min, scent_map_boundary_max );
return scent_map_boundaries.contains( p.xy() );
}
void scent_map::update( const tripoint ¢er, map &m )
{
// Stop updating scent after X turns of the player not moving.
// Once wind is added, need to reset this on wind shifts as well.
if( !player_last_position || center != *player_last_position ) {
player_last_position.emplace( center );
player_last_moved = calendar::turn;
} else if( player_last_moved + 1000_turns < calendar::turn ) {
return;
}
//the block and reduce scent properties are folded into a single scent_transfer value here
//block=0 reduce=1 normal=5
scent_array<char> scent_transfer;
std::array < std::array < int, 3 + SCENT_RADIUS * 2 >, 1 + SCENT_RADIUS * 2 > new_scent;
std::array < std::array < int, 3 + SCENT_RADIUS * 2 >, 1 + SCENT_RADIUS * 2 > sum_3_scent_y;
std::array < std::array < char, 3 + SCENT_RADIUS * 2 >, 1 + SCENT_RADIUS * 2 > squares_used_y;
diagonal_blocks( &blocked_cache )[MAPSIZE_X][MAPSIZE_Y] = m.access_cache(
center.z ).vehicle_obstructed_cache;
// for loop constants
const int scentmap_minx = center.x - SCENT_RADIUS;
const int scentmap_maxx = center.x + SCENT_RADIUS;
const int scentmap_miny = center.y - SCENT_RADIUS;
const int scentmap_maxy = center.y + SCENT_RADIUS;
// The new scent flag searching function. Should be wayyy faster than the old one.
m.scent_blockers( scent_transfer, point( scentmap_minx - 1, scentmap_miny - 1 ),
point( scentmap_maxx + 1, scentmap_maxy + 1 ) );
for( int x = 0; x < SCENT_RADIUS * 2 + 3; ++x ) {
sum_3_scent_y[0][x] = 0;
squares_used_y[0][x] = 0;
sum_3_scent_y[SCENT_RADIUS * 2][x] = 0;
squares_used_y[SCENT_RADIUS * 2][x] = 0;
}
for( int x = 0; x < SCENT_RADIUS * 2 + 3; ++x ) {
for( int y = 0; y < SCENT_RADIUS * 2 + 1; ++y ) {
point abs( x + scentmap_minx - 1, y + scentmap_miny );
// remember the sum of the scent val for the 3 neighboring squares that can defuse into
sum_3_scent_y[y][x] = 0;
squares_used_y[y][x] = 0;
for( int i = abs.y - 1; i <= abs.y + 1; ++i ) {
sum_3_scent_y[y][x] += scent_transfer[abs.x][i] * grscent[abs.x][i];
squares_used_y[y][x] += scent_transfer[abs.x][i];
}
}
}
for( int x = 1; x < SCENT_RADIUS * 2 + 2; ++x ) {
for( int y = 0; y < SCENT_RADIUS * 2 + 1; ++y ) {
const point abs( x + scentmap_minx - 1, y + scentmap_miny );
int squares_used = squares_used_y[y][x - 1] + squares_used_y[y][x] + squares_used_y[y][x + 1];
int total = sum_3_scent_y[y][x - 1] + sum_3_scent_y[y][x] + sum_3_scent_y[y][x + 1];
//handle vehicle holes
if( blocked_cache[abs.x][abs.y].nw && scent_transfer[abs.x + 1][abs.y + 1] == 5 ) {
squares_used -= 4;
total -= 4 * grscent[abs.x + 1][abs.y + 1];
}
if( blocked_cache[abs.x][abs.y].ne && scent_transfer[abs.x - 1][abs.y + 1] == 5 ) {
squares_used -= 4;
total -= 4 * grscent[abs.x - 1][abs.y + 1];
}
if( blocked_cache[abs.x - 1][abs.y - 1].nw && scent_transfer[abs.x - 1][abs.y - 1] == 5 ) {
squares_used -= 4;
total -= 4 * grscent[abs.x - 1][abs.y - 1];
}
if( blocked_cache[abs.x + 1][abs.y - 1].ne && scent_transfer[abs.x + 1][abs.y - 1] == 5 ) {
squares_used -= 4;
total -= 4 * grscent[abs.x + 1][abs.y - 1];
}
//Lingering scent
int temp_scent = grscent[abs.x][abs.y] * ( 250 - squares_used *
scent_transfer[abs.x][abs.y] ) ;
temp_scent -= grscent[abs.x][abs.y] * scent_transfer[abs.x][abs.y] *
( 45 - squares_used ) / 5;
new_scent[y][x] = ( temp_scent + total * scent_transfer[abs.x][abs.y] ) / 250;
}
}
for( int x = 1; x < SCENT_RADIUS * 2 + 2; ++x ) {
for( int y = 0; y < SCENT_RADIUS * 2 + 1; ++y ) {
grscent[x + scentmap_minx - 1 ][y + scentmap_miny] = new_scent[y][x];
}
}
}
namespace
{
generic_factory<scent_type> scent_factory( "scent_type" );
} // namespace
template<>
const scent_type &string_id<scent_type>::obj() const
{
return scent_factory.obj( *this );
}
template<>
bool string_id<scent_type>::is_valid() const
{
return scent_factory.is_valid( *this );
}
void scent_type::load_scent_type( const JsonObject &jo, const std::string &src )
{
scent_factory.load( jo, src );
}
void scent_type::load( const JsonObject &jo, const std::string & )
{
assign( jo, "id", id );
assign( jo, "receptive_species", receptive_species );
}
const std::vector<scent_type> &scent_type::get_all()
{
return scent_factory.get_all();
}
void scent_type::check_scent_consistency()
{
for( const scent_type &styp : get_all() ) {
for( const species_id &spe : styp.receptive_species ) {
if( !spe.is_valid() ) {
debugmsg( "scent_type %s has invalid species_id %s in receptive_species", styp.id.c_str(),
spe.c_str() );
}
}
}
}
void scent_type::reset()
{
scent_factory.reset();
}