1 #include "AppHdr.h"
2
3 #include "jobs.h"
4
5 #include "errors.h"
6 #include "item-prop.h"
7 #include "libutil.h"
8 #include "mapdef.h"
9 #include "ng-setup.h"
10 #include "playable.h"
11 #include "player.h"
12 #include "spl-book.h"
13 #include "stringutil.h"
14
15 #include "job-data.h"
16
_job_def(job_type job)17 static const job_def& _job_def(job_type job)
18 {
19 ASSERT_RANGE(job, 0, NUM_JOBS);
20 return job_data.at(job);
21 }
22
get_job_abbrev(job_type which_job)23 const char *get_job_abbrev(job_type which_job)
24 {
25 if (which_job == JOB_UNKNOWN)
26 return "Un";
27 return _job_def(which_job).abbrev;
28 }
29
get_job_by_abbrev(const char * abbrev)30 job_type get_job_by_abbrev(const char *abbrev)
31 {
32 for (auto& entry : job_data)
33 if (lowercase_string(abbrev) == lowercase_string(entry.second.abbrev))
34 return entry.first;
35
36 return JOB_UNKNOWN;
37 }
38
get_job_name(job_type which_job)39 const char *get_job_name(job_type which_job)
40 {
41 if (which_job == JOB_UNKNOWN)
42 return "Unemployed";
43
44 return _job_def(which_job).name;
45 }
46
get_job_by_name(const char * name)47 job_type get_job_by_name(const char *name)
48 {
49 job_type job = JOB_UNKNOWN;
50
51 const string low_name = lowercase_string(name);
52
53 for (auto& entry : job_data)
54 {
55 string low_job = lowercase_string(entry.second.name);
56
57 const size_t pos = low_job.find(low_name);
58 if (pos != string::npos)
59 {
60 job = entry.first;
61 if (!pos) // prefix takes preference
62 break;
63 }
64 }
65
66 return job;
67 }
68
69 // Must be called after species_stat_init for the wanderer formula to work.
job_stat_init(job_type job)70 void job_stat_init(job_type job)
71 {
72 rng::subgenerator stat_rng;
73
74 you.hp_max_adj_perm = 0;
75
76 you.base_stats[STAT_STR] += _job_def(job).s;
77 you.base_stats[STAT_INT] += _job_def(job).i;
78 you.base_stats[STAT_DEX] += _job_def(job).d;
79
80 if (job == JOB_WANDERER)
81 {
82 for (int i = 0; i < 12; i++)
83 {
84 const auto stat = random_choose_weighted(
85 you.base_stats[STAT_STR] > 17 ? 1 : 2, STAT_STR,
86 you.base_stats[STAT_INT] > 17 ? 1 : 2, STAT_INT,
87 you.base_stats[STAT_DEX] > 17 ? 1 : 2, STAT_DEX);
88 you.base_stats[stat]++;
89 }
90 }
91 }
92
job_has_weapon_choice(job_type job)93 bool job_has_weapon_choice(job_type job)
94 {
95 return _job_def(job).wchoice != WCHOICE_NONE;
96 }
97
job_gets_good_weapons(job_type job)98 bool job_gets_good_weapons(job_type job)
99 {
100 return _job_def(job).wchoice == WCHOICE_GOOD;
101 }
102
job_gets_ranged_weapons(job_type job)103 bool job_gets_ranged_weapons(job_type job)
104 {
105 return _job_def(job).wchoice == WCHOICE_RANGED;
106 }
107
give_job_equipment(job_type job)108 void give_job_equipment(job_type job)
109 {
110 item_list items;
111 for (const string& it : _job_def(job).equipment)
112 items.add_item(it);
113 for (size_t i = 0; i < items.size(); i++)
114 {
115 const item_spec spec = items.get_item(i);
116 int plus = 0;
117 if (spec.props.exists("charges"))
118 plus = spec.props["charges"];
119 if (spec.props.exists("plus"))
120 plus = spec.props["plus"];
121 newgame_make_item(spec.base_type, spec.sub_type, max(spec.qty, 1),
122 plus, spec.ego);
123 }
124 }
125
126 // Must be called after equipment is given for weapon skill to be given.
give_job_skills(job_type job)127 void give_job_skills(job_type job)
128 {
129 for (const pair<skill_type, int>& entry : _job_def(job).skills)
130 {
131 skill_type skill = entry.first;
132 int amount = entry.second;
133 if (skill == SK_WEAPON)
134 {
135 const item_def *weap = you.weapon();
136 skill = weap ? item_attack_skill(*weap) : SK_UNARMED_COMBAT;
137 //XXX: WTF?
138 if (you.has_mutation(MUT_NO_GRASPING) && job == JOB_FIGHTER)
139 amount += 2;
140 // Don't give throwing hunters Short Blades skill.
141 if (job_gets_ranged_weapons(job) && !(weap && is_range_weapon(*weap)))
142 skill = SK_THROWING;
143 }
144 you.skills[skill] += amount;
145 }
146 }
147
get_job_spells(job_type job)148 vector<spell_type> get_job_spells(job_type job)
149 {
150 return _job_def(job).library;
151 }
152
debug_jobdata()153 void debug_jobdata()
154 {
155 string fails;
156
157 for (int i = 0; i < NUM_JOBS; i++)
158 if (!job_data.count(static_cast<job_type>(i)))
159 fails += "job number " + to_string(i) + "is not present\n";
160
161 item_list dummy;
162 for (auto& entry : job_data)
163 for (const string& it : entry.second.equipment)
164 {
165 const string error = dummy.add_item(it, false);
166 if (!error.empty())
167 fails += error + "\n";
168 }
169
170 dump_test_fails(fails, "job-data");
171 }
172
job_recommends_species(job_type job,species_type species)173 bool job_recommends_species(job_type job, species_type species)
174 {
175 return find(_job_def(job).recommended_species.begin(),
176 _job_def(job).recommended_species.end(),
177 species) != _job_def(job).recommended_species.end();
178 }
179
180 // A random valid (selectable on the new game screen) job.
random_starting_job()181 job_type random_starting_job()
182 {
183 const auto jobs = playable_jobs();
184 return jobs[random2(jobs.size())];
185 }
186
is_starting_job(job_type job)187 bool is_starting_job(job_type job)
188 {
189 return job_type_valid(job) // Ensure the job isn't RANDOM/VIABLE/UNKNOWN
190 && !job_is_removed(job);
191 }
192
job_is_removed(job_type job)193 bool job_is_removed(job_type job)
194 {
195 return _job_def(job).recommended_species.empty();
196 }
197
job_recommended_species(job_type job)198 vector<species_type> job_recommended_species(job_type job)
199 {
200 return _job_def(job).recommended_species;
201 }
202