Apply the new ground_level method.
[crawl.git] / crawl-ref / source / tiledoll.cc
blobb0ddcdc1bd42757fd6b73422f0c314f1fa02f19f
1 /*
2 * File: tiledoll.cc
3 * Summary: Region system implementations
5 * Created by: ennewalker on Sat Jan 5 01:33:53 2008 UTC
6 */
8 #include "AppHdr.h"
10 #ifdef USE_TILE
12 #include "tiledoll.h"
14 #include <sys/stat.h>
16 #include "files.h"
17 #include "tilebuf.h"
18 #include "tiledef-player.h"
19 #include "tilepick-p.h"
20 #include "transform.h"
22 dolls_data::dolls_data()
24 parts = new tileidx_t[TILEP_PART_MAX];
25 memset(parts, 0, TILEP_PART_MAX * sizeof(int));
28 dolls_data::dolls_data(const dolls_data& _orig)
30 parts = new tileidx_t[TILEP_PART_MAX];
31 memcpy(parts, _orig.parts, TILEP_PART_MAX * sizeof(int));
34 const dolls_data& dolls_data::operator=(const dolls_data& other)
36 memcpy(parts, other.parts, TILEP_PART_MAX * sizeof(int));
37 return (*this);
40 dolls_data::~dolls_data()
42 delete[] parts;
43 parts = NULL;
46 dolls_data player_doll;
48 bool save_doll_data(int mode, int num, const dolls_data* dolls)
50 // Save mode, num, and all dolls into dolls.txt.
51 std::string dollsTxtString = datafile_path("dolls.txt", false, true);
53 struct stat stFileInfo;
54 stat(dollsTxtString.c_str(), &stFileInfo);
56 // Write into the current directory instead if we didn't find the file
57 // or don't have write permissions.
58 const char *dollsTxt = (dollsTxtString.c_str()[0] == 0
59 || !(stFileInfo.st_mode & S_IWUSR)) ? "dolls.txt"
60 : dollsTxtString.c_str();
62 FILE *fp = NULL;
63 if ((fp = fopen(dollsTxt, "w+")) != NULL)
65 fprintf(fp, "MODE=%s\n",
66 (mode == TILEP_MODE_EQUIP) ? "EQUIP" :
67 (mode == TILEP_MODE_LOADING) ? "LOADING"
68 : "DEFAULT");
70 fprintf(fp, "NUM=%02d\n", num == -1 ? 0 : num);
72 // Print some explanatory comments. May contain no spaces!
73 fprintf(fp, "#Legend:\n");
74 fprintf(fp, "#***:equipment/123:index/000:none\n");
75 fprintf(fp, "#Shadow/Base/Cloak/Boots/Legs/Body/Gloves/Weapon/Shield/Hair/Beard/Helmet/Halo/Enchant/DrcHead/DrcWing\n");
76 fprintf(fp, "#Sh:Bse:Clk:Bts:Leg:Bdy:Glv:Wpn:Shd:Hai:Brd:Hlm:Hal:Enc:Drc:Wng\n");
77 char fbuf[80];
78 for (unsigned int i = 0; i < NUM_MAX_DOLLS; ++i)
80 tilep_print_parts(fbuf, dolls[i]);
81 fprintf(fp, "%s", fbuf);
83 fclose(fp);
85 return (true);
88 return (false);
91 bool load_doll_data(const char *fn, dolls_data *dolls, int max,
92 tile_doll_mode *mode, int *cur)
94 char fbuf[1024];
95 FILE *fp = NULL;
97 std::string dollsTxtString = datafile_path(fn, false, true);
99 struct stat stFileInfo;
100 stat(dollsTxtString.c_str(), &stFileInfo);
102 // Try to read from the current directory instead if we didn't find the
103 // file or don't have reading permissions.
104 const char *dollsTxt = (dollsTxtString.c_str()[0] == 0
105 || !(stFileInfo.st_mode & S_IRUSR)) ? "dolls.txt"
106 : dollsTxtString.c_str();
109 if ((fp = fopen(dollsTxt, "r")) == NULL)
111 // File doesn't exist. By default, use equipment settings.
112 *mode = TILEP_MODE_EQUIP;
113 return (false);
115 else
117 memset(fbuf, 0, sizeof(fbuf));
118 // Read mode from file.
119 if (fscanf(fp, "%1023s", fbuf) != EOF)
121 if (strcmp(fbuf, "MODE=DEFAULT") == 0)
122 *mode = TILEP_MODE_DEFAULT;
123 else if (strcmp(fbuf, "MODE=EQUIP") == 0)
124 *mode = TILEP_MODE_EQUIP; // Nothing else to be done.
126 // Read current doll from file.
127 if (fscanf(fp, "%1023s", fbuf) != EOF)
129 if (strncmp(fbuf, "NUM=", 4) == 0)
131 sscanf(fbuf, "NUM=%d", cur);
132 if (*cur < 0 || *cur >= NUM_MAX_DOLLS)
133 *cur = 0;
137 if (max == 1)
139 // Load only one doll, either the one defined by NUM or
140 // use the default/equipment setting.
141 if (*mode != TILEP_MODE_LOADING)
143 if (*mode == TILEP_MODE_DEFAULT)
144 tilep_job_default(you.char_class, &dolls[0]);
146 // If we don't need to load a doll, return now.
147 fclose(fp);
148 return (true);
151 int count = 0;
152 while (fscanf(fp, "%1023s", fbuf) != EOF)
154 if (fbuf[0] == '#') // Skip comment lines.
155 continue;
157 if (*cur == count++)
159 tilep_scan_parts(fbuf, dolls[0], you.species,
160 you.experience_level);
161 break;
165 else // Load up to max dolls from file.
167 for (int count = 0; count < max && fscanf(fp, "%1023s", fbuf) != EOF;)
169 if (fbuf[0] == '#') // Skip comment lines.
170 continue;
172 tilep_scan_parts(fbuf, dolls[count++], you.species,
173 you.experience_level);
177 fclose(fp);
178 return (true);
182 void init_player_doll()
184 dolls_data dolls[NUM_MAX_DOLLS];
186 for (int i = 0; i < NUM_MAX_DOLLS; i++)
187 for (int j = 0; j < TILEP_PART_MAX; j++)
188 dolls[i].parts[j] = TILEP_SHOW_EQUIP;
190 tile_doll_mode mode = TILEP_MODE_LOADING;
191 int cur = 0;
192 load_doll_data("dolls.txt", dolls, NUM_MAX_DOLLS, &mode, &cur);
194 if (mode == TILEP_MODE_LOADING)
196 player_doll = dolls[cur];
197 tilep_race_default(you.species, you.experience_level, &player_doll);
198 return;
201 for (int i = 0; i < TILEP_PART_MAX; i++)
202 player_doll.parts[i] = TILEP_SHOW_EQUIP;
203 tilep_race_default(you.species, you.experience_level, &player_doll);
205 if (mode == TILEP_MODE_EQUIP)
206 return;
208 tilep_job_default(you.char_class, &player_doll);
211 static int _get_random_doll_part(int p)
213 ASSERT(p >= 0 && p <= TILEP_PART_MAX);
214 return (tile_player_part_start[p]
215 + random2(tile_player_part_count[p]));
218 static void _fill_doll_part(dolls_data &doll, int p)
220 ASSERT(p >= 0 && p <= TILEP_PART_MAX);
221 doll.parts[p] = _get_random_doll_part(p);
224 void create_random_doll(dolls_data &rdoll)
226 // All dolls roll for these.
227 _fill_doll_part(rdoll, TILEP_PART_BODY);
228 _fill_doll_part(rdoll, TILEP_PART_HAND1);
229 _fill_doll_part(rdoll, TILEP_PART_LEG);
230 _fill_doll_part(rdoll, TILEP_PART_BOOTS);
231 _fill_doll_part(rdoll, TILEP_PART_HAIR);
233 // The following are only rolled with 50% chance.
234 if (coinflip())
235 _fill_doll_part(rdoll, TILEP_PART_CLOAK);
236 if (coinflip())
237 _fill_doll_part(rdoll, TILEP_PART_ARM);
238 if (coinflip())
239 _fill_doll_part(rdoll, TILEP_PART_HAND2);
240 if (coinflip())
241 _fill_doll_part(rdoll, TILEP_PART_HELM);
243 if (one_chance_in(4))
244 _fill_doll_part(rdoll, TILEP_PART_BEARD);
247 // Deterministically pick a pair of trousers for this character to use
248 // for SHOW_EQUIP, as there's no corresponding item slot for this.
249 static tileidx_t _random_trousers()
251 int offset = static_cast<int>(you.species) * 9887
252 + static_cast<int>(you.char_class) * 8719;
253 const char *name = you.your_name.c_str();
254 for (int i = 0; i < 8 && *name; ++i, ++name)
255 offset += name[i] * 4643;
257 const int range = TILEP_LEG_LAST_NORM - TILEP_LEG_FIRST_NORM + 1;
258 return (TILEP_LEG_FIRST_NORM + offset % range);
261 void fill_doll_equipment(dolls_data &result)
263 // Base tile.
264 if (result.parts[TILEP_PART_BASE] == TILEP_SHOW_EQUIP)
266 tilep_race_default(you.species, you.experience_level, &result);
269 // Main hand.
270 if (result.parts[TILEP_PART_HAND1] == TILEP_SHOW_EQUIP)
272 const int item = you.equip[EQ_WEAPON];
273 if (you.form == TRAN_BLADE_HANDS)
274 result.parts[TILEP_PART_HAND1] = TILEP_HAND1_BLADEHAND;
275 else if (item == -1)
276 result.parts[TILEP_PART_HAND1] = 0;
277 else
278 result.parts[TILEP_PART_HAND1] = tilep_equ_weapon(you.inv[item]);
280 // Off hand.
281 if (result.parts[TILEP_PART_HAND2] == TILEP_SHOW_EQUIP)
283 const int item = you.equip[EQ_SHIELD];
284 if (you.form == TRAN_BLADE_HANDS)
285 result.parts[TILEP_PART_HAND2] = TILEP_HAND2_BLADEHAND;
286 else if (item == -1)
287 result.parts[TILEP_PART_HAND2] = 0;
288 else
289 result.parts[TILEP_PART_HAND2] = tilep_equ_shield(you.inv[item]);
291 // Body armour.
292 if (result.parts[TILEP_PART_BODY] == TILEP_SHOW_EQUIP)
294 const int item = you.equip[EQ_BODY_ARMOUR];
295 if (item == -1)
296 result.parts[TILEP_PART_BODY] = 0;
297 else
298 result.parts[TILEP_PART_BODY] = tilep_equ_armour(you.inv[item]);
300 // Cloak.
301 if (result.parts[TILEP_PART_CLOAK] == TILEP_SHOW_EQUIP)
303 const int item = you.equip[EQ_CLOAK];
304 if (item == -1)
305 result.parts[TILEP_PART_CLOAK] = 0;
306 else
307 result.parts[TILEP_PART_CLOAK] = tilep_equ_cloak(you.inv[item]);
309 // Helmet.
310 if (result.parts[TILEP_PART_HELM] == TILEP_SHOW_EQUIP)
312 const int item = you.equip[EQ_HELMET];
313 if (item != -1)
315 result.parts[TILEP_PART_HELM] = tilep_equ_helm(you.inv[item]);
317 else if (player_mutation_level(MUT_HORNS) > 0)
319 switch (player_mutation_level(MUT_HORNS))
321 case 1:
322 result.parts[TILEP_PART_HELM] = TILEP_HELM_HORNS1;
323 break;
324 case 2:
325 result.parts[TILEP_PART_HELM] = TILEP_HELM_HORNS2;
326 break;
327 case 3:
328 result.parts[TILEP_PART_HELM] = TILEP_HELM_HORNS3;
329 break;
332 else
334 result.parts[TILEP_PART_HELM] = 0;
337 // Leg.
338 if (result.parts[TILEP_PART_LEG] == TILEP_SHOW_EQUIP)
340 result.parts[TILEP_PART_LEG] = _random_trousers();
342 // Boots.
343 if (result.parts[TILEP_PART_BOOTS] == TILEP_SHOW_EQUIP)
345 const int item = you.equip[EQ_BOOTS];
346 if (item != -1)
347 result.parts[TILEP_PART_BOOTS] = tilep_equ_boots(you.inv[item]);
348 else if (player_mutation_level(MUT_HOOVES) >= 3)
349 result.parts[TILEP_PART_BOOTS] = TILEP_BOOTS_HOOVES;
350 else
351 result.parts[TILEP_PART_BOOTS] = 0;
353 // Gloves.
354 if (result.parts[TILEP_PART_ARM] == TILEP_SHOW_EQUIP)
356 const int item = you.equip[EQ_GLOVES];
357 if (item != -1)
358 result.parts[TILEP_PART_ARM] = tilep_equ_gloves(you.inv[item]);
359 else if (you.has_claws(false) >= 3)
360 result.parts[TILEP_PART_ARM] = TILEP_ARM_CLAWS;
361 else
362 result.parts[TILEP_PART_ARM] = 0;
364 // Halo.
365 if (result.parts[TILEP_PART_HALO] == TILEP_SHOW_EQUIP)
367 const bool halo = you.haloed();
368 result.parts[TILEP_PART_HALO] = halo ? TILEP_HALO_TSO : 0;
370 // Enchantments.
371 if (result.parts[TILEP_PART_ENCH] == TILEP_SHOW_EQUIP)
373 result.parts[TILEP_PART_ENCH] =
374 (you.duration[DUR_LIQUID_FLAMES] ? TILEP_ENCH_STICKY_FLAME : 0);
376 // Draconian head/wings.
377 if (player_genus(GENPC_DRACONIAN))
379 tileidx_t base = 0;
380 tileidx_t head = 0;
381 tileidx_t wing = 0;
382 tilep_draconian_init(you.species, you.experience_level,
383 &base, &head, &wing);
385 if (result.parts[TILEP_PART_DRCHEAD] == TILEP_SHOW_EQUIP)
386 result.parts[TILEP_PART_DRCHEAD] = head;
387 if (result.parts[TILEP_PART_DRCWING] == TILEP_SHOW_EQUIP)
388 result.parts[TILEP_PART_DRCWING] = wing;
390 // Shadow.
391 if (result.parts[TILEP_PART_SHADOW] == TILEP_SHOW_EQUIP)
392 result.parts[TILEP_PART_SHADOW] = TILEP_SHADOW_SHADOW;
394 // Various other slots.
395 for (int i = 0; i < TILEP_PART_MAX; i++)
396 if (result.parts[i] == TILEP_SHOW_EQUIP)
397 result.parts[i] = 0;
400 // Writes equipment information into per-character doll file.
401 void save_doll_file(writer &dollf)
403 dolls_data result = player_doll;
404 fill_doll_equipment(result);
406 // Write into file.
407 char fbuf[80];
408 tilep_print_parts(fbuf, result);
409 dollf.write(fbuf, strlen(fbuf));
411 if (you.attribute[ATTR_HELD] > 0)
412 dollf.write("net\n", 4);
415 void pack_doll_buf(SubmergedTileBuffer& buf, const dolls_data &doll, int x, int y, bool submerged, bool ghost)
417 // Ordered from back to front.
418 int p_order[TILEP_PART_MAX] =
420 // background
421 TILEP_PART_SHADOW,
422 TILEP_PART_HALO,
423 TILEP_PART_ENCH,
424 TILEP_PART_DRCWING,
425 TILEP_PART_CLOAK,
426 // player
427 TILEP_PART_BASE,
428 TILEP_PART_BOOTS,
429 TILEP_PART_LEG,
430 TILEP_PART_BODY,
431 TILEP_PART_ARM,
432 TILEP_PART_HAIR,
433 TILEP_PART_BEARD,
434 TILEP_PART_HELM,
435 TILEP_PART_HAND1,
436 TILEP_PART_HAND2,
437 TILEP_PART_DRCHEAD
440 int flags[TILEP_PART_MAX];
441 tilep_calc_flags(doll, flags);
443 // For skirts, boots go under the leg armour. For pants, they go over.
444 if (doll.parts[TILEP_PART_LEG] < TILEP_LEG_SKIRT_OFS)
446 p_order[7] = TILEP_PART_BOOTS;
447 p_order[6] = TILEP_PART_LEG;
450 // Special case bardings from being cut off.
451 const bool is_naga = is_player_tile(doll.parts[TILEP_PART_BASE],
452 TILEP_BASE_NAGA);
454 if (doll.parts[TILEP_PART_BOOTS] >= TILEP_BOOTS_NAGA_BARDING
455 && doll.parts[TILEP_PART_BOOTS] <= TILEP_BOOTS_NAGA_BARDING_RED)
457 flags[TILEP_PART_BOOTS] = is_naga ? TILEP_FLAG_NORMAL : TILEP_FLAG_HIDE;
460 const bool is_cent = is_player_tile(doll.parts[TILEP_PART_BASE],
461 TILEP_BASE_CENTAUR);
463 if (doll.parts[TILEP_PART_BOOTS] >= TILEP_BOOTS_CENTAUR_BARDING
464 && doll.parts[TILEP_PART_BOOTS] <= TILEP_BOOTS_CENTAUR_BARDING_RED)
466 flags[TILEP_PART_BOOTS] = is_cent ? TILEP_FLAG_NORMAL : TILEP_FLAG_HIDE;
469 // A higher index here means that the part should be drawn on top.
470 // This is drawn in reverse order because this could be a ghost
471 // or being drawn in water, in which case we want the top-most part
472 // to blend with the background underneath and not with the parts
473 // underneath. Parts drawn afterwards will not obscure parts drawn
474 // previously, because "i" is passed as the depth below.
475 for (int i = TILEP_PART_MAX - 1; i >= 0; --i)
477 int p = p_order[i];
479 if (!doll.parts[p] || flags[p] == TILEP_FLAG_HIDE)
480 continue;
482 if (p == TILEP_PART_SHADOW && (submerged || ghost))
483 continue;
485 int ymax = TILE_Y;
487 if (flags[p] == TILEP_FLAG_CUT_CENTAUR
488 || flags[p] == TILEP_FLAG_CUT_NAGA)
490 ymax = 18;
493 buf.add(doll.parts[p], x, y, i, submerged, ghost, 0, 0, ymax);
497 #endif