1 /*
2  *  File:       stuff.cc
3  *  Summary:    Misc stuff.
4  *  Written by: Linley Henzell
5  *
6  *  Change History (most recent first):
7  *
8  *   <4>    11/14/99     cdl    added random40(), made arg to random*() signed
9  *   <3>    11/06/99     cdl    added random22()
10  *   <2>     9/25/99     cdl    linuxlib -> liblinux
11  *   <1>     -/--/--     LRH    Created
12  */
13 
14 #include "AppHdr.h"
15 #include "stuff.h"
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <ctype.h>
21 
22 // may need this later for something else {dlb}:
23 // required for table_lookup() {dlb}
24 //#include <stdarg.h>
25 // required for table_lookup() {dlb}
26 
27 #ifdef DOS
28 #include <conio.h>
29 #endif
30 
31 #ifdef LINUX
32 #include "liblinux.h"
33 #endif
34 
35 #include "externs.h"
36 
37 #include "macro.h"
38 #include "misc.h"
39 #include "monstuff.h"
40 #include "mon-util.h"
41 #include "output.h"
42 #include "skills2.h"
43 #include "view.h"
44 
45 
46 // required for stuff::coinflip() and cf_setseed()
47 unsigned long cfseed;
48 
49 // unfortunately required for near_stairs(ugh!):
50 extern unsigned char (*mapch) (unsigned char);
51 
52 // Crude, but functional.
make_time_string(time_t abs_time,char * const buff,int buff_size)53 char *const make_time_string( time_t abs_time, char *const buff, int buff_size )
54 {
55     const int days  = abs_time / 86400;
56     const int hours = (abs_time % 86400) / 3600;
57     const int mins  = (abs_time % 3600) / 60;
58     const int secs  = abs_time % 60;
59 
60     char day_buff[32];
61 
62     if (days > 0)
63     {
64         snprintf( day_buff, sizeof(day_buff), "%d day%s, ",
65                   days, (days > 1) ? "s" : "" );
66     }
67 
68     snprintf( buff, buff_size, "%s%02d:%02d:%02d",
69               (days > 0) ? day_buff : "", hours, mins, secs );
70 
71     return (buff);
72 }
73 
set_redraw_status(unsigned long flags)74 void set_redraw_status( unsigned long flags )
75 {
76     you.redraw_status_flags |= flags;
77 }
78 
tag_followers(void)79 void tag_followers( void )
80 {
81     int count_x, count_y;
82 
83     for (count_x = you.x_pos - 1; count_x <= you.x_pos + 1; count_x++)
84     {
85         for (count_y = you.y_pos - 1; count_y <= you.y_pos + 1; count_y++)
86         {
87             if (count_x == you.x_pos && count_y == you.y_pos)
88                 continue;
89 
90             if (mgrd[count_x][count_y] == NON_MONSTER)
91                 continue;
92 
93             struct monsters *fmenv = &menv[mgrd[count_x][count_y]];
94 
95             if ((fmenv->type == MONS_PANDEMONIUM_DEMON)
96                 || (fmenv->type == MONS_PLANT)
97                 || (fmenv->type == MONS_FUNGUS)
98                 || (fmenv->type == MONS_OKLOB_PLANT)
99                 || (fmenv->type == MONS_CURSE_SKULL)
100                 || (fmenv->type == MONS_PLAYER_GHOST)  // cdl
101                 || (fmenv->type == MONS_CURSE_TOE)
102                 || (fmenv->type == MONS_POTION_MIMIC)
103                 || (fmenv->type == MONS_WEAPON_MIMIC)
104                 || (fmenv->type == MONS_ARMOUR_MIMIC)
105                 || (fmenv->type == MONS_SCROLL_MIMIC)
106                 || (fmenv->type == MONS_GOLD_MIMIC)
107                 || (fmenv->type == -1))
108             {
109                 continue;
110             }
111 
112             if (monster_habitat(fmenv->type) != DNGN_FLOOR)
113                 continue;
114 
115             if (fmenv->speed_increment < 50)
116                 continue;
117 
118             // only friendly monsters,  or those actively seeking the
119             // player,  will follow up/down stairs.
120             if (!(mons_friendly(fmenv) ||
121                 (fmenv->behaviour == BEH_SEEK && fmenv->foe == MHITYOU)))
122             {
123                 continue;
124             }
125 
126             // monster is chasing player through stairs:
127             fmenv->flags |= MF_TAKING_STAIRS;
128 
129 #if DEBUG_DIAGNOSTICS
130             snprintf( info, INFO_SIZE, "%s is marked for following.",
131                       ptr_monam( fmenv, DESC_CAP_THE ) );
132             mpr( info, MSGCH_DIAGNOSTICS );
133 #endif
134         }
135     }
136 }
137 
untag_followers(void)138 void untag_followers( void )
139 {
140     for (int m = 0; m < MAX_MONSTERS; m++)
141     {
142         struct monsters *mon = &menv[m];
143         mon->flags &= (~MF_TAKING_STAIRS);
144     }
145 }
146 
get_ch(void)147 unsigned char get_ch(void)
148 {
149     unsigned char gotched = getch();
150 
151     if (gotched == 0)
152         gotched = getch();
153 
154     return gotched;
155 }                               // end get_ch()
156 
random2(int max)157 int random2(int max)
158 {
159 #ifdef USE_NEW_RANDOM
160     //return (int) ((((float) max) * rand()) / RAND_MAX); - this is bad!
161     // Uses FP, so is horribly slow on computers without coprocessors.
162     // Taken from comp.lang.c FAQ. May have problems as max approaches
163     // RAND_MAX, but this is rather unlikely.
164     // We've used rand() rather than random() for the portability, I think.
165 
166     if (max < 1 || max >= RAND_MAX)
167         return 0;
168     else
169         return (int) rand() / (RAND_MAX / max + 1);
170 #else
171 
172     if (max < 1)
173         return 0;
174 
175     return rand() % max;
176 #endif
177 }
178 
179 // random2avg() returns same mean value as random2() but with a  lower variance
180 // never use with rolls < 2 as that would be silly - use random2() instead {dlb}
random2avg(int max,int rolls)181 int random2avg(int max, int rolls)
182 {
183     int sum = 0;
184 
185     sum += random2(max);
186 
187     for (int i = 0; i < (rolls - 1); i++)
188     {
189         sum += random2(max + 1);
190     }
191 
192     return (sum / rolls);
193 }
194 
roll_dice(int num,int size)195 int roll_dice( int num, int size )
196 {
197     int ret = 0;
198     int i;
199 
200     // If num <= 0 or size <= 0, then we'll just return the default
201     // value of zero.  This is good behaviour in that it will be
202     // appropriate for calculated values that might be passed in.
203     if (num > 0 && size > 0)
204     {
205         ret += num;     // since random2() is zero based
206 
207         for (i = 0; i < num; i++)
208             ret += random2( size );
209     }
210 
211     return (ret);
212 }
213 
roll_dice(const struct dice_def & dice)214 int roll_dice( const struct dice_def &dice )
215 {
216     return (roll_dice( dice.num, dice.size ));
217 }
218 
219 // originally designed to randomize evasion -
220 // values are slightly lowered near (max) and
221 // approach an upper limit somewhere near (limit/2)
random2limit(int max,int limit)222 int random2limit(int max, int limit)
223 {
224     int i;
225     int sum = 0;
226 
227     if (max < 1)
228         return 0;
229 
230     for (i = 0; i < max; i++)
231     {
232         if (random2(limit) >= i)
233             sum++;
234     }
235 
236     return sum;
237 }                               // end random2limit()
238 
239 // answers the question: "Is a grid within character's line of sight?"
see_grid(unsigned char grx,unsigned char gry)240 bool see_grid(unsigned char grx, unsigned char gry)
241 {
242     if (grx > you.x_pos - 9 && grx < you.x_pos + 9
243                         && gry > you.y_pos - 9 && gry < you.y_pos + 9)
244     {
245         if (env.show[grx - you.x_pos + 9][gry - you.y_pos + 9] != 0)
246             return true;
247 
248         // rare case: can player see self?  (of course!)
249         if (grx == you.x_pos && gry == you.y_pos)
250             return true;
251     }
252 
253     return false;
254 }                               // end see_grid()
255 
end(int end_arg)256 void end(int end_arg)
257 {
258 #ifdef LINUX
259     lincurses_shutdown();
260 #endif
261 
262 #ifdef MAC
263     deinit_mac();
264 #endif
265 
266 #ifdef WIN32CONSOLE
267     deinit_libw32c();
268 #endif
269 
270     exit(end_arg);
271 }
272 
redraw_screen(void)273 void redraw_screen(void)
274 {
275 #ifdef PLAIN_TERM
276 // this function is used for systems without gettext/puttext to redraw the
277 // playing screen after a call to for example inventory.
278     draw_border();
279 
280     you.redraw_hit_points = 1;
281     you.redraw_magic_points = 1;
282     you.redraw_strength = 1;
283     you.redraw_intelligence = 1;
284     you.redraw_dexterity = 1;
285     you.redraw_armour_class = 1;
286     you.redraw_evasion = 1;
287     you.redraw_gold = 1;
288     you.redraw_experience = 1;
289     you.wield_change = true;
290 
291     set_redraw_status( REDRAW_LINE_1_MASK | REDRAW_LINE_2_MASK | REDRAW_LINE_3_MASK );
292 
293     print_stats();
294 
295     if (Options.delay_message_clear)
296         mesclr( true );
297 
298     new_level();
299 
300     viewwindow(1, false);
301 #endif
302 }                               // end redraw_screen()
303 
304 // STEPDOWN FUNCTION to replace conditional chains in spells2.cc 12jan2000 {dlb}
305 // it is a bit more extensible and optimizes the logical structure, as well
306 // usage: summon_swarm() summon_undead() summon_scorpions() summon_things()
307 // ex(1): stepdown_value (foo, 2, 2, 6, 8) replaces the following block:
308 //
309 
310 /*
311    if (foo > 2)
312      foo = (foo - 2) / 2 + 2;
313    if (foo > 4)
314      foo = (foo - 4) / 2 + 4;
315    if (foo > 6)
316      foo = (foo - 6) / 2 + 6;
317    if (foo > 8)
318      foo = 8;
319  */
320 
321 //
322 // ex(2): bar = stepdown_value(bar, 2, 2, 6, -1) replaces the following block:
323 //
324 
325 /*
326    if (bar > 2)
327      bar = (bar - 2) / 2 + 2;
328    if (bar > 4)
329      bar = (bar - 4) / 2 + 4;
330    if (bar > 6)
331      bar = (bar - 6) / 2 + 6;
332  */
333 
334 // I hope this permits easier/more experimentation with value stepdowns in
335 // the code it really needs to be rewritten to accept arbitrary (unevenly
336 // spaced) steppings
stepdown_value(int base_value,int stepping,int first_step,int last_step,int ceiling_value)337 int stepdown_value(int base_value, int stepping, int first_step,
338                    int last_step, int ceiling_value)
339 {
340     int return_value = base_value;
341 
342     // values up to the first "step" returned unchanged:
343     if (return_value <= first_step)
344         return return_value;
345 
346     for (int this_step = first_step; this_step <= last_step;
347                                                     this_step += stepping)
348     {
349         if (return_value > this_step)
350             return_value = ((return_value - this_step) / 2) + this_step;
351         else
352             break;              // exit loop iff value fully "stepped down"
353     }
354 
355     // "no final ceiling" == -1
356     if (ceiling_value != -1 && return_value > ceiling_value)
357         return ceiling_value;   // highest value to return is "ceiling"
358     else
359         return return_value;    // otherwise, value returned "as is"
360 
361 }                               // end stepdown_value()
362 
363 
364 // I got so tired of seeing: ".. && random2(foo) == 0 && .." in the code
365 // that I broke down and wrote this little -- very little -- function.
366 // anyway, I think improving the readability of the code offsets whatever
367 // overhead the additional (intermediary) function call added to Crawl -
368 // we'll just make it up by tightening code elsewhere, right guys?
369 // [use with == and != only .. never directly with comparisons or math]
370 //                                                      -- 14jan2000 {dlb}
one_chance_in(int a_million)371 bool one_chance_in(int a_million)
372 {
373     return (random2(a_million) == 0);
374 }                               // end one_chance_in() - that's it? :P {dlb}
375 
376 // I got to thinking a bit more about how much people talk
377 // about RNGs and RLs and also about the issue of performance
378 // when it comes to Crawl's RNG ... turning to *Numerical
379 // Recipies in C* (Chapter 7-4, page 298), I hit upon what
380 // struck me as a fine solution.
381 
382 // You can read all the details about this function (pretty
383 // much stolen shamelessly from NRinC) elsewhere, but having
384 // tested it out myself I think it satisfies Crawl's incessant
385 // need to decide things on a 50-50 flip of the coin. No call
386 // to random2() required -- along with all that wonderful math
387 // and type casting -- and only a single variable its pointer,
388 // and some bitwise operations to randomly generate 1s and 0s!
389 // No parameter passing, nothing. Too good to be true, but it
390 // works as long as cfseed is not set to absolute zero when it
391 // is initialized ... good for 2**n-1 random bits before the
392 // pattern repeats (n = long's bitlength on your platform).
393 // It also avoids problems with poor implementations of rand()
394 // on some platforms in regards to low-order bits ... a big
395 // problem if one is only looking for a 1 or a 0 with random2()!
396 
397 // Talk about a hard sell! Anyway, it returns bool, so please
398 // use appropriately -- I set it to bool to prevent such
399 // tomfoolery, as I think that pure RNG and quickly grabbing
400 // either a value of 1 or 0 should be separated where possible
401 // to lower overhead in Crawl ... at least until it assembles
402 // itself into something a bit more orderly :P 16jan2000 {dlb}
403 
404 // NB(1): cfseed is defined atop stuff.cc
405 // NB(2): IB(foo) and MASK are defined somewhere in defines.h
406 // NB(3): the function assumes that cf_setseed() has been called
407 //        beforehand - the call is presently made in acr::initialise()
408 //        right after srandom() and srand() are called (note also
409 //        that cf_setseed() requires rand() - random2 returns int
410 //        but a long can't hurt there).
coinflip(void)411 bool coinflip(void)
412 {
413     extern unsigned long cfseed;        // defined atop stuff.cc
414     unsigned long *ptr_cfseed = &cfseed;
415 
416     if (*ptr_cfseed & IB18)
417     {
418         *ptr_cfseed = ((*ptr_cfseed ^ MASK) << 1) | IB1;
419         return true;
420     }
421     else
422     {
423         *ptr_cfseed <<= 1;
424         return false;
425     }
426 }                               // end coinflip()
427 
428 // cf_setseed should only be called but once in all of Crawl!!! {dlb}
cf_setseed(void)429 void cf_setseed(void)
430 {
431     extern unsigned long cfseed;        // defined atop stuff.cc
432     unsigned long *ptr_cfseed = &cfseed;
433 
434     do
435     {
436         // using rand() here makes these predictable -- bwr
437         *ptr_cfseed = rand();
438     }
439     while (*ptr_cfseed == 0);
440 }
441 
442 // simple little function to quickly modify all three stats
443 // at once - does check for '0' modifiers to prevent needless
444 // adding .. could use checking for sums less than zero, I guess.
445 // used in conjunction with newgame::species_stat_init() and
446 // newgame::job_stat_init() routines 24jan2000 {dlb}
modify_all_stats(int STmod,int IQmod,int DXmod)447 void modify_all_stats(int STmod, int IQmod, int DXmod)
448 {
449     if (STmod)
450     {
451         you.strength += STmod;
452         you.max_strength += STmod;
453         you.redraw_strength = 1;
454     }
455 
456     if (IQmod)
457     {
458         you.intel += IQmod;
459         you.max_intel += IQmod;
460         you.redraw_intelligence = 1;
461     }
462 
463     if (DXmod)
464     {
465         you.dex += DXmod;
466         you.max_dex += DXmod;
467         you.redraw_dexterity = 1;
468     }
469 
470     return;
471 }                               // end modify_stat()
472 
canned_msg(unsigned char which_message)473 void canned_msg(unsigned char which_message)
474 {
475     switch (which_message)
476     {
477     case MSG_SOMETHING_APPEARS:
478         strcpy(info, "Something appears ");
479         strcat(info, (you.species == SP_NAGA || you.species == SP_CENTAUR)
480                                             ? "before you" : "at your feet");
481         strcat(info, "!");
482         mpr(info);
483         break;
484 
485     case MSG_NOTHING_HAPPENS:
486         mpr("Nothing appears to happen.");
487         break;
488     case MSG_YOU_RESIST:
489         mpr("You resist.");
490         break;
491     case MSG_TOO_BERSERK:
492         mpr("You are too berserk!");
493         break;
494     case MSG_NOTHING_CARRIED:
495         mpr("You aren't carrying anything.");
496         break;
497     case MSG_CANNOT_DO_YET:
498         mpr("You can't do that yet.");
499         break;
500     case MSG_OK:
501         mpr("Okay, then.");
502         break;
503     case MSG_UNTHINKING_ACT:
504         mpr("Why would you want to do that?");
505         break;
506     case MSG_SPELL_FIZZLES:
507         mpr("The spell fizzles.");
508         break;
509     case MSG_HUH:
510         mpr("Huh?");
511         break;
512     case MSG_EMPTY_HANDED:
513         mpr("You are now empty-handed.");
514         break;
515     }
516 
517     return;
518 }                               // end canned_msg()
519 
520 // jmf: general helper (should be used all over in code)
521 //      -- idea borrowed from Nethack
yesno(const char * str,bool safe,bool clear_after)522 bool yesno( const char *str, bool safe, bool clear_after )
523 {
524     unsigned char tmp;
525 
526     for (;;)
527     {
528         mpr(str, MSGCH_PROMPT);
529 
530         tmp = (unsigned char) getch();
531 
532         if (Options.easy_confirm == CONFIRM_ALL_EASY
533             || (Options.easy_confirm == CONFIRM_SAFE_EASY && safe))
534         {
535             tmp = toupper( tmp );
536         }
537 
538         if (clear_after)
539             mesclr();
540 
541         if (tmp == 'N')
542             return false;
543         else if (tmp == 'Y')
544             return true;
545         else
546             mpr("[Y]es or [N]o only, please.");
547     }
548 }                               // end yesno()
549 
550 // More accurate than distance() given the actual movement geonmetry -- bwr
grid_distance(int x,int y,int x2,int y2)551 int grid_distance( int x, int y, int x2, int y2 )
552 {
553     const int dx = abs( x - x2 );
554     const int dy = abs( y - y2 );
555 
556     // returns distance in terms of moves:
557     return ((dx > dy) ? dx : dy);
558 }
559 
distance(int x,int y,int x2,int y2)560 int distance( int x, int y, int x2, int y2 )
561 {
562     //jmf: now accurate, but remember to only compare vs. pre-squared distances.
563     //     thus, next to == (distance(m1.x,m1.y, m2.x,m2.y) <= 2)
564     const int dx = x - x2;
565     const int dy = y - y2;
566 
567     return ((dx * dx) + (dy * dy));
568 }                               // end distance()
569 
adjacent(int x,int y,int x2,int y2)570 bool adjacent( int x, int y, int x2, int y2 )
571 {
572     return (abs(x - x2) <= 1 && abs(y - y2) <= 1);
573 }
574 
silenced(char x,char y)575 bool silenced(char x, char y)
576 {
577 #ifdef USE_SILENCE_CODE
578 
579     if (you.duration[DUR_SILENCE] > 0
580         && distance(x, y, you.x_pos, you.y_pos) <= 36)  // (6 * 6)
581     {
582         return true;
583     }
584     else
585     {
586         //else // FIXME: implement, and let monsters cast, too
587         //  for (int i = 0; i < MAX_SILENCES; i++)
588         //  {
589         //      if (distance(x, y, silencer[i].x, silencer[i].y) <= 36)
590         //         return true;
591         //  }
592         return false;
593     }
594 
595 #else
596     return false;
597 #endif
598 }                               // end silenced()
599 
player_can_hear(char x,char y)600 bool player_can_hear(char x, char y)
601 {
602 #ifdef USE_SILENCE_CODE
603     return (!silenced(x, y) && !silenced(you.x_pos, you.y_pos));
604 #else
605     return true;
606 #endif
607 }                               // end player_can_hear()
608 
random_colour(void)609 unsigned char random_colour(void)
610 {
611     return (1 + random2(15));
612 }                               // end random_colour()
613 
index_to_letter(int the_index)614 char index_to_letter(int the_index)
615 {
616     return (the_index + ((the_index < 26) ? 'a' : ('A' - 26)));
617 }                               // end index_to_letter()
618 
letter_to_index(int the_letter)619 int letter_to_index(int the_letter)
620 {
621     if (the_letter >= 'a' && the_letter <= 'z')
622         // returns range [0-25] {dlb}
623         the_letter -= 'a';
624     else if (the_letter >= 'A' && the_letter <= 'Z')
625         // returns range [26-51] {dlb}
626         the_letter -= ('A' - 26);
627 
628     return the_letter;
629 }                               // end letter_to_index()
630 
631 // returns 0 if the point is not near stairs
632 // returns 1 if the point is near unoccupied stairs
633 // returns 2 if the point is near player-occupied stairs
634 
near_stairs(int px,int py,int max_dist,unsigned char & stair_gfx)635 int near_stairs(int px, int py, int max_dist, unsigned char &stair_gfx)
636 {
637     int i,j;
638 
639     for(i=-max_dist; i<=max_dist; i++)
640     {
641         for(j=-max_dist; j<=max_dist; j++)
642         {
643             int x = px + i;
644             int y = py + j;
645 
646             if (x<0 || x>=GXM || y<0 || y>=GYM)
647                 continue;
648 
649             // very simple check
650             if (grd[x][y] >= DNGN_STONE_STAIRS_DOWN_I
651                 && grd[x][y] <= DNGN_RETURN_FROM_SWAMP
652                 && grd[x][y] != DNGN_ENTER_SHOP)        // silly
653             {
654                 stair_gfx = mapch(grd[x][y]);
655                 return ((x == you.x_pos && y == you.y_pos) ? 2 : 1);
656             }
657         }
658     }
659 
660     return false;
661 }
662