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