summaryrefslogtreecommitdiffstats
path: root/trunk/source/stuff.cc
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/source/stuff.cc')
-rw-r--r--trunk/source/stuff.cc661
1 files changed, 661 insertions, 0 deletions
diff --git a/trunk/source/stuff.cc b/trunk/source/stuff.cc
new file mode 100644
index 0000000000..4ad0e2a394
--- /dev/null
+++ b/trunk/source/stuff.cc
@@ -0,0 +1,661 @@
+/*
+ * File: stuff.cc
+ * Summary: Misc stuff.
+ * Written by: Linley Henzell
+ *
+ * Change History (most recent first):
+ *
+ * <4> 11/14/99 cdl added random40(), made arg to random*() signed
+ * <3> 11/06/99 cdl added random22()
+ * <2> 9/25/99 cdl linuxlib -> liblinux
+ * <1> -/--/-- LRH Created
+ */
+
+#include "AppHdr.h"
+#include "stuff.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+// may need this later for something else {dlb}:
+// required for table_lookup() {dlb}
+//#include <stdarg.h>
+// required for table_lookup() {dlb}
+
+#ifdef DOS
+#include <conio.h>
+#endif
+
+#ifdef LINUX
+#include "liblinux.h"
+#endif
+
+#include "externs.h"
+
+#include "macro.h"
+#include "misc.h"
+#include "monstuff.h"
+#include "mon-util.h"
+#include "output.h"
+#include "skills2.h"
+#include "view.h"
+
+
+// required for stuff::coinflip() and cf_setseed()
+unsigned long cfseed;
+
+// unfortunately required for near_stairs(ugh!):
+extern unsigned char (*mapch) (unsigned char);
+
+// Crude, but functional.
+char *const make_time_string( time_t abs_time, char *const buff, int buff_size )
+{
+ const int days = abs_time / 86400;
+ const int hours = (abs_time % 86400) / 3600;
+ const int mins = (abs_time % 3600) / 60;
+ const int secs = abs_time % 60;
+
+ char day_buff[32];
+
+ if (days > 0)
+ {
+ snprintf( day_buff, sizeof(day_buff), "%d day%s, ",
+ days, (days > 1) ? "s" : "" );
+ }
+
+ snprintf( buff, buff_size, "%s%02d:%02d:%02d",
+ (days > 0) ? day_buff : "", hours, mins, secs );
+
+ return (buff);
+}
+
+void set_redraw_status( unsigned long flags )
+{
+ you.redraw_status_flags |= flags;
+}
+
+void tag_followers( void )
+{
+ int count_x, count_y;
+
+ for (count_x = you.x_pos - 1; count_x <= you.x_pos + 1; count_x++)
+ {
+ for (count_y = you.y_pos - 1; count_y <= you.y_pos + 1; count_y++)
+ {
+ if (count_x == you.x_pos && count_y == you.y_pos)
+ continue;
+
+ if (mgrd[count_x][count_y] == NON_MONSTER)
+ continue;
+
+ struct monsters *fmenv = &menv[mgrd[count_x][count_y]];
+
+ if ((fmenv->type == MONS_PANDEMONIUM_DEMON)
+ || (fmenv->type == MONS_PLANT)
+ || (fmenv->type == MONS_FUNGUS)
+ || (fmenv->type == MONS_OKLOB_PLANT)
+ || (fmenv->type == MONS_CURSE_SKULL)
+ || (fmenv->type == MONS_PLAYER_GHOST) // cdl
+ || (fmenv->type == MONS_CURSE_TOE)
+ || (fmenv->type == MONS_POTION_MIMIC)
+ || (fmenv->type == MONS_WEAPON_MIMIC)
+ || (fmenv->type == MONS_ARMOUR_MIMIC)
+ || (fmenv->type == MONS_SCROLL_MIMIC)
+ || (fmenv->type == MONS_GOLD_MIMIC)
+ || (fmenv->type == -1))
+ {
+ continue;
+ }
+
+ if (monster_habitat(fmenv->type) != DNGN_FLOOR)
+ continue;
+
+ if (fmenv->speed_increment < 50)
+ continue;
+
+ // only friendly monsters, or those actively seeking the
+ // player, will follow up/down stairs.
+ if (!(mons_friendly(fmenv) ||
+ (fmenv->behaviour == BEH_SEEK && fmenv->foe == MHITYOU)))
+ {
+ continue;
+ }
+
+ // monster is chasing player through stairs:
+ fmenv->flags |= MF_TAKING_STAIRS;
+
+#if DEBUG_DIAGNOSTICS
+ snprintf( info, INFO_SIZE, "%s is marked for following.",
+ ptr_monam( fmenv, DESC_CAP_THE ) );
+ mpr( info, MSGCH_DIAGNOSTICS );
+#endif
+ }
+ }
+}
+
+void untag_followers( void )
+{
+ for (int m = 0; m < MAX_MONSTERS; m++)
+ {
+ struct monsters *mon = &menv[m];
+ mon->flags &= (~MF_TAKING_STAIRS);
+ }
+}
+
+unsigned char get_ch(void)
+{
+ unsigned char gotched = getch();
+
+ if (gotched == 0)
+ gotched = getch();
+
+ return gotched;
+} // end get_ch()
+
+int random2(int max)
+{
+#ifdef USE_NEW_RANDOM
+ //return (int) ((((float) max) * rand()) / RAND_MAX); - this is bad!
+ // Uses FP, so is horribly slow on computers without coprocessors.
+ // Taken from comp.lang.c FAQ. May have problems as max approaches
+ // RAND_MAX, but this is rather unlikely.
+ // We've used rand() rather than random() for the portability, I think.
+
+ if (max < 1 || max >= RAND_MAX)
+ return 0;
+ else
+ return (int) rand() / (RAND_MAX / max + 1);
+#else
+
+ if (max < 1)
+ return 0;
+
+ return rand() % max;
+#endif
+}
+
+// random2avg() returns same mean value as random2() but with a lower variance
+// never use with rolls < 2 as that would be silly - use random2() instead {dlb}
+int random2avg(int max, int rolls)
+{
+ int sum = 0;
+
+ sum += random2(max);
+
+ for (int i = 0; i < (rolls - 1); i++)
+ {
+ sum += random2(max + 1);
+ }
+
+ return (sum / rolls);
+}
+
+int roll_dice( int num, int size )
+{
+ int ret = 0;
+ int i;
+
+ // If num <= 0 or size <= 0, then we'll just return the default
+ // value of zero. This is good behaviour in that it will be
+ // appropriate for calculated values that might be passed in.
+ if (num > 0 && size > 0)
+ {
+ ret += num; // since random2() is zero based
+
+ for (i = 0; i < num; i++)
+ ret += random2( size );
+ }
+
+ return (ret);
+}
+
+int roll_dice( const struct dice_def &dice )
+{
+ return (roll_dice( dice.num, dice.size ));
+}
+
+// originally designed to randomize evasion -
+// values are slightly lowered near (max) and
+// approach an upper limit somewhere near (limit/2)
+int random2limit(int max, int limit)
+{
+ int i;
+ int sum = 0;
+
+ if (max < 1)
+ return 0;
+
+ for (i = 0; i < max; i++)
+ {
+ if (random2(limit) >= i)
+ sum++;
+ }
+
+ return sum;
+} // end random2limit()
+
+// answers the question: "Is a grid within character's line of sight?"
+bool see_grid(unsigned char grx, unsigned char gry)
+{
+ if (grx > you.x_pos - 9 && grx < you.x_pos + 9
+ && gry > you.y_pos - 9 && gry < you.y_pos + 9)
+ {
+ if (env.show[grx - you.x_pos + 9][gry - you.y_pos + 9] != 0)
+ return true;
+
+ // rare case: can player see self? (of course!)
+ if (grx == you.x_pos && gry == you.y_pos)
+ return true;
+ }
+
+ return false;
+} // end see_grid()
+
+void end(int end_arg)
+{
+#ifdef LINUX
+ lincurses_shutdown();
+#endif
+
+#ifdef MAC
+ deinit_mac();
+#endif
+
+#ifdef WIN32CONSOLE
+ deinit_libw32c();
+#endif
+
+ exit(end_arg);
+}
+
+void redraw_screen(void)
+{
+#ifdef PLAIN_TERM
+// this function is used for systems without gettext/puttext to redraw the
+// playing screen after a call to for example inventory.
+ draw_border();
+
+ you.redraw_hit_points = 1;
+ you.redraw_magic_points = 1;
+ you.redraw_strength = 1;
+ you.redraw_intelligence = 1;
+ you.redraw_dexterity = 1;
+ you.redraw_armour_class = 1;
+ you.redraw_evasion = 1;
+ you.redraw_gold = 1;
+ you.redraw_experience = 1;
+ you.wield_change = true;
+
+ set_redraw_status( REDRAW_LINE_1_MASK | REDRAW_LINE_2_MASK | REDRAW_LINE_3_MASK );
+
+ print_stats();
+
+ if (Options.delay_message_clear)
+ mesclr( true );
+
+ new_level();
+
+ viewwindow(1, false);
+#endif
+} // end redraw_screen()
+
+// STEPDOWN FUNCTION to replace conditional chains in spells2.cc 12jan2000 {dlb}
+// it is a bit more extensible and optimizes the logical structure, as well
+// usage: summon_swarm() summon_undead() summon_scorpions() summon_things()
+// ex(1): stepdown_value (foo, 2, 2, 6, 8) replaces the following block:
+//
+
+/*
+ if (foo > 2)
+ foo = (foo - 2) / 2 + 2;
+ if (foo > 4)
+ foo = (foo - 4) / 2 + 4;
+ if (foo > 6)
+ foo = (foo - 6) / 2 + 6;
+ if (foo > 8)
+ foo = 8;
+ */
+
+//
+// ex(2): bar = stepdown_value(bar, 2, 2, 6, -1) replaces the following block:
+//
+
+/*
+ if (bar > 2)
+ bar = (bar - 2) / 2 + 2;
+ if (bar > 4)
+ bar = (bar - 4) / 2 + 4;
+ if (bar > 6)
+ bar = (bar - 6) / 2 + 6;
+ */
+
+// I hope this permits easier/more experimentation with value stepdowns in
+// the code it really needs to be rewritten to accept arbitrary (unevenly
+// spaced) steppings
+int stepdown_value(int base_value, int stepping, int first_step,
+ int last_step, int ceiling_value)
+{
+ int return_value = base_value;
+
+ // values up to the first "step" returned unchanged:
+ if (return_value <= first_step)
+ return return_value;
+
+ for (int this_step = first_step; this_step <= last_step;
+ this_step += stepping)
+ {
+ if (return_value > this_step)
+ return_value = ((return_value - this_step) / 2) + this_step;
+ else
+ break; // exit loop iff value fully "stepped down"
+ }
+
+ // "no final ceiling" == -1
+ if (ceiling_value != -1 && return_value > ceiling_value)
+ return ceiling_value; // highest value to return is "ceiling"
+ else
+ return return_value; // otherwise, value returned "as is"
+
+} // end stepdown_value()
+
+
+// I got so tired of seeing: ".. && random2(foo) == 0 && .." in the code
+// that I broke down and wrote this little -- very little -- function.
+// anyway, I think improving the readability of the code offsets whatever
+// overhead the additional (intermediary) function call added to Crawl -
+// we'll just make it up by tightening code elsewhere, right guys?
+// [use with == and != only .. never directly with comparisons or math]
+// -- 14jan2000 {dlb}
+bool one_chance_in(int a_million)
+{
+ return (random2(a_million) == 0);
+} // end one_chance_in() - that's it? :P {dlb}
+
+// I got to thinking a bit more about how much people talk
+// about RNGs and RLs and also about the issue of performance
+// when it comes to Crawl's RNG ... turning to *Numerical
+// Recipies in C* (Chapter 7-4, page 298), I hit upon what
+// struck me as a fine solution.
+
+// You can read all the details about this function (pretty
+// much stolen shamelessly from NRinC) elsewhere, but having
+// tested it out myself I think it satisfies Crawl's incessant
+// need to decide things on a 50-50 flip of the coin. No call
+// to random2() required -- along with all that wonderful math
+// and type casting -- and only a single variable its pointer,
+// and some bitwise operations to randomly generate 1s and 0s!
+// No parameter passing, nothing. Too good to be true, but it
+// works as long as cfseed is not set to absolute zero when it
+// is initialized ... good for 2**n-1 random bits before the
+// pattern repeats (n = long's bitlength on your platform).
+// It also avoids problems with poor implementations of rand()
+// on some platforms in regards to low-order bits ... a big
+// problem if one is only looking for a 1 or a 0 with random2()!
+
+// Talk about a hard sell! Anyway, it returns bool, so please
+// use appropriately -- I set it to bool to prevent such
+// tomfoolery, as I think that pure RNG and quickly grabbing
+// either a value of 1 or 0 should be separated where possible
+// to lower overhead in Crawl ... at least until it assembles
+// itself into something a bit more orderly :P 16jan2000 {dlb}
+
+// NB(1): cfseed is defined atop stuff.cc
+// NB(2): IB(foo) and MASK are defined somewhere in defines.h
+// NB(3): the function assumes that cf_setseed() has been called
+// beforehand - the call is presently made in acr::initialise()
+// right after srandom() and srand() are called (note also
+// that cf_setseed() requires rand() - random2 returns int
+// but a long can't hurt there).
+bool coinflip(void)
+{
+ extern unsigned long cfseed; // defined atop stuff.cc
+ unsigned long *ptr_cfseed = &cfseed;
+
+ if (*ptr_cfseed & IB18)
+ {
+ *ptr_cfseed = ((*ptr_cfseed ^ MASK) << 1) | IB1;
+ return true;
+ }
+ else
+ {
+ *ptr_cfseed <<= 1;
+ return false;
+ }
+} // end coinflip()
+
+// cf_setseed should only be called but once in all of Crawl!!! {dlb}
+void cf_setseed(void)
+{
+ extern unsigned long cfseed; // defined atop stuff.cc
+ unsigned long *ptr_cfseed = &cfseed;
+
+ do
+ {
+ // using rand() here makes these predictable -- bwr
+ *ptr_cfseed = rand();
+ }
+ while (*ptr_cfseed == 0);
+}
+
+// simple little function to quickly modify all three stats
+// at once - does check for '0' modifiers to prevent needless
+// adding .. could use checking for sums less than zero, I guess.
+// used in conjunction with newgame::species_stat_init() and
+// newgame::job_stat_init() routines 24jan2000 {dlb}
+void modify_all_stats(int STmod, int IQmod, int DXmod)
+{
+ if (STmod)
+ {
+ you.strength += STmod;
+ you.max_strength += STmod;
+ you.redraw_strength = 1;
+ }
+
+ if (IQmod)
+ {
+ you.intel += IQmod;
+ you.max_intel += IQmod;
+ you.redraw_intelligence = 1;
+ }
+
+ if (DXmod)
+ {
+ you.dex += DXmod;
+ you.max_dex += DXmod;
+ you.redraw_dexterity = 1;
+ }
+
+ return;
+} // end modify_stat()
+
+void canned_msg(unsigned char which_message)
+{
+ switch (which_message)
+ {
+ case MSG_SOMETHING_APPEARS:
+ strcpy(info, "Something appears ");
+ strcat(info, (you.species == SP_NAGA || you.species == SP_CENTAUR)
+ ? "before you" : "at your feet");
+ strcat(info, "!");
+ mpr(info);
+ break;
+
+ case MSG_NOTHING_HAPPENS:
+ mpr("Nothing appears to happen.");
+ break;
+ case MSG_YOU_RESIST:
+ mpr("You resist.");
+ break;
+ case MSG_TOO_BERSERK:
+ mpr("You are too berserk!");
+ break;
+ case MSG_NOTHING_CARRIED:
+ mpr("You aren't carrying anything.");
+ break;
+ case MSG_CANNOT_DO_YET:
+ mpr("You can't do that yet.");
+ break;
+ case MSG_OK:
+ mpr("Okay, then.");
+ break;
+ case MSG_UNTHINKING_ACT:
+ mpr("Why would you want to do that?");
+ break;
+ case MSG_SPELL_FIZZLES:
+ mpr("The spell fizzles.");
+ break;
+ case MSG_HUH:
+ mpr("Huh?");
+ break;
+ case MSG_EMPTY_HANDED:
+ mpr("You are now empty-handed.");
+ break;
+ }
+
+ return;
+} // end canned_msg()
+
+// jmf: general helper (should be used all over in code)
+// -- idea borrowed from Nethack
+bool yesno( const char *str, bool safe, bool clear_after )
+{
+ unsigned char tmp;
+
+ for (;;)
+ {
+ mpr(str, MSGCH_PROMPT);
+
+ tmp = (unsigned char) getch();
+
+ if (Options.easy_confirm == CONFIRM_ALL_EASY
+ || (Options.easy_confirm == CONFIRM_SAFE_EASY && safe))
+ {
+ tmp = toupper( tmp );
+ }
+
+ if (clear_after)
+ mesclr();
+
+ if (tmp == 'N')
+ return false;
+ else if (tmp == 'Y')
+ return true;
+ else
+ mpr("[Y]es or [N]o only, please.");
+ }
+} // end yesno()
+
+// More accurate than distance() given the actual movement geonmetry -- bwr
+int grid_distance( int x, int y, int x2, int y2 )
+{
+ const int dx = abs( x - x2 );
+ const int dy = abs( y - y2 );
+
+ // returns distance in terms of moves:
+ return ((dx > dy) ? dx : dy);
+}
+
+int distance( int x, int y, int x2, int y2 )
+{
+ //jmf: now accurate, but remember to only compare vs. pre-squared distances.
+ // thus, next to == (distance(m1.x,m1.y, m2.x,m2.y) <= 2)
+ const int dx = x - x2;
+ const int dy = y - y2;
+
+ return ((dx * dx) + (dy * dy));
+} // end distance()
+
+bool adjacent( int x, int y, int x2, int y2 )
+{
+ return (abs(x - x2) <= 1 && abs(y - y2) <= 1);
+}
+
+bool silenced(char x, char y)
+{
+#ifdef USE_SILENCE_CODE
+
+ if (you.duration[DUR_SILENCE] > 0
+ && distance(x, y, you.x_pos, you.y_pos) <= 36) // (6 * 6)
+ {
+ return true;
+ }
+ else
+ {
+ //else // FIXME: implement, and let monsters cast, too
+ // for (int i = 0; i < MAX_SILENCES; i++)
+ // {
+ // if (distance(x, y, silencer[i].x, silencer[i].y) <= 36)
+ // return true;
+ // }
+ return false;
+ }
+
+#else
+ return false;
+#endif
+} // end silenced()
+
+bool player_can_hear(char x, char y)
+{
+#ifdef USE_SILENCE_CODE
+ return (!silenced(x, y) && !silenced(you.x_pos, you.y_pos));
+#else
+ return true;
+#endif
+} // end player_can_hear()
+
+unsigned char random_colour(void)
+{
+ return (1 + random2(15));
+} // end random_colour()
+
+char index_to_letter(int the_index)
+{
+ return (the_index + ((the_index < 26) ? 'a' : ('A' - 26)));
+} // end index_to_letter()
+
+int letter_to_index(int the_letter)
+{
+ if (the_letter >= 'a' && the_letter <= 'z')
+ // returns range [0-25] {dlb}
+ the_letter -= 'a';
+ else if (the_letter >= 'A' && the_letter <= 'Z')
+ // returns range [26-51] {dlb}
+ the_letter -= ('A' - 26);
+
+ return the_letter;
+} // end letter_to_index()
+
+// returns 0 if the point is not near stairs
+// returns 1 if the point is near unoccupied stairs
+// returns 2 if the point is near player-occupied stairs
+
+int near_stairs(int px, int py, int max_dist, unsigned char &stair_gfx)
+{
+ int i,j;
+
+ for(i=-max_dist; i<=max_dist; i++)
+ {
+ for(j=-max_dist; j<=max_dist; j++)
+ {
+ int x = px + i;
+ int y = py + j;
+
+ if (x<0 || x>=GXM || y<0 || y>=GYM)
+ continue;
+
+ // very simple check
+ if (grd[x][y] >= DNGN_STONE_STAIRS_DOWN_I
+ && grd[x][y] <= DNGN_RETURN_FROM_SWAMP
+ && grd[x][y] != DNGN_ENTER_SHOP) // silly
+ {
+ stair_gfx = mapch(grd[x][y]);
+ return ((x == you.x_pos && y == you.y_pos) ? 2 : 1);
+ }
+ }
+ }
+
+ return false;
+}