forked from cataclysmbnteam/Cataclysm-BN
-
Notifications
You must be signed in to change notification settings - Fork 0
/
anatomy.cpp
169 lines (140 loc) · 4.09 KB
/
anatomy.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
#include "anatomy.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <numeric>
#include <set>
#include "cata_utility.h"
#include "debug.h"
#include "enums.h"
#include "generic_factory.h"
#include "int_id.h"
#include "json.h"
#include "messages.h"
#include "rng.h"
#include "type_id.h"
#include "weighted_list.h"
anatomy_id human_anatomy( "human_anatomy" );
namespace
{
generic_factory<anatomy> anatomy_factory( "anatomy" );
} // namespace
template<>
bool anatomy_id::is_valid() const
{
return anatomy_factory.is_valid( *this );
}
template<>
const anatomy &anatomy_id::obj() const
{
return anatomy_factory.obj( *this );
}
void anatomy::load_anatomy( const JsonObject &jo, const std::string &src )
{
anatomy_factory.load( jo, src );
}
void anatomy::load( const JsonObject &jo, const std::string & )
{
mandatory( jo, was_loaded, "id", id );
mandatory( jo, was_loaded, "parts", unloaded_bps );
}
void anatomy::reset()
{
anatomy_factory.reset();
}
void anatomy::finalize_all()
{
// For some weird reason, generic_factory::finalize doesn't call finalize
anatomy_factory.finalize();
for( const anatomy &an : anatomy_factory.get_all() ) {
const_cast<anatomy &>( an ).finalize();
}
}
void anatomy::finalize()
{
size_sum = 0.0f;
cached_bps.clear();
for( const bodypart_str_id &id : unloaded_bps ) {
if( id.is_valid() ) {
add_body_part( id );
} else {
debugmsg( "Anatomy %s has invalid body part %s", id.c_str(), id.c_str() );
}
}
unloaded_bps.clear();
}
void anatomy::check_consistency()
{
anatomy_factory.check();
if( !human_anatomy.is_valid() ) {
debugmsg( "Could not load human anatomy, expect crash" );
}
}
void anatomy::check() const
{
if( !get_part_with_cumulative_hit_size( size_sum ).is_valid() ) {
debugmsg( "Invalid size_sum calculation for anatomy %s", id.c_str() );
}
for( size_t i = 0; i < 3; i++ ) {
const float size_all = std::accumulate( cached_bps.begin(), cached_bps.end(), 0.0f, [i]( float acc,
const bodypart_id & bp ) {
return acc + bp->hit_size_relative[i];
} );
if( size_all <= 0.0f ) {
debugmsg( "Anatomy %s has no part hittable when size difference is %d", id.c_str(),
static_cast<int>( i ) - 1 );
}
}
}
std::vector<bodypart_id> anatomy::get_bodyparts() const
{
return cached_bps;
}
void anatomy::add_body_part( const bodypart_str_id &new_bp )
{
cached_bps.emplace_back( new_bp.id() );
const body_part_type &bp_struct = new_bp.obj();
size_sum += bp_struct.hit_size;
}
// TODO: get_function_with_better_name
bodypart_str_id anatomy::get_part_with_cumulative_hit_size( float size ) const
{
for( const bodypart_id &part : cached_bps ) {
size -= part->hit_size;
if( size <= 0.0f ) {
return part.id();
}
}
return bodypart_str_id::NULL_ID();
}
bodypart_id anatomy::random_body_part() const
{
return get_part_with_cumulative_hit_size( rng_float( 0.0f, size_sum ) ).id();
}
bodypart_id anatomy::select_body_part( int size_diff, int hit_roll ) const
{
const size_t size_diff_index = static_cast<size_t>( 1 + clamp( size_diff, -1, 1 ) );
weighted_float_list<bodypart_id> hit_weights;
for( const bodypart_id &bp : cached_bps ) {
float weight = bp->hit_size_relative[size_diff_index];
if( weight <= 0.0f ) {
continue;
}
if( hit_roll != 0 ) {
weight *= std::pow( hit_roll, bp->hit_difficulty );
}
hit_weights.add( bp, weight );
}
// Debug for seeing weights.
for( const weighted_object<double, bodypart_id> &pr : hit_weights ) {
add_msg( m_debug, "%s = %.3f", pr.obj.obj().name, pr.weight );
}
const bodypart_id *ret = hit_weights.pick();
if( ret == nullptr ) {
debugmsg( "Attempted to select body part from empty anatomy %s", id.c_str() );
return bodypart_str_id::NULL_ID().id();
}
add_msg( m_debug, "selected part: %s", ret->id().obj().name );
return *ret;
}