#include "AppHdr.h"
#include "beam.h"
#include "cio.h"
#include "coordit.h"
#include "database.h"
#include "directn.h"
#include "los.h"
#include "message.h"
#include "misc.h"
#include "monplace.h"
#include "state.h"
#include "stuff.h"
#include "view.h"
#include <cstdarg>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <stack>
#ifdef TARGET_OS_DOS
#include <conio.h>
#endif
#ifdef UNIX
#ifndef USE_TILE
#include "libunix.h"
#endif
#endif
#include "branch.h"
#include "delay.h"
#include "externs.h"
#include "itemprop.h"
#include "items.h"
#include "macro.h"
#include "misc.h"
#include "monstuff.h"
#include "mon-util.h"
#include "notes.h"
#include "output.h"
#include "player.h"
#include "religion.h"
#include "tutorial.h"
#include "view.h"
stack_iterator::stack_iterator(const coord_def& pos)
{
cur_link = igrd(pos);
if ( cur_link != NON_ITEM )
next_link = mitm[cur_link].link;
else
next_link = NON_ITEM;
}
stack_iterator::stack_iterator(int start_link)
{
cur_link = start_link;
if ( cur_link != NON_ITEM )
next_link = mitm[cur_link].link;
else
next_link = NON_ITEM;
}
stack_iterator::operator bool() const
{
return ( cur_link != NON_ITEM );
}
item_def& stack_iterator::operator*() const
{
ASSERT( cur_link != NON_ITEM );
return mitm[cur_link];
}
item_def* stack_iterator::operator->() const
{
ASSERT( cur_link != NON_ITEM );
return &mitm[cur_link];
}
int stack_iterator::link() const
{
return cur_link;
}
const stack_iterator& stack_iterator::operator ++ ()
{
cur_link = next_link;
if ( cur_link != NON_ITEM )
next_link = mitm[cur_link].link;
return *this;
}
stack_iterator stack_iterator::operator++(int dummy)
{
const stack_iterator copy = *this;
++(*this);
return copy;
}
std::string make_time_string(time_t abs_time, bool terse)
{
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;
std::ostringstream buff;
buff << std::setfill('0');
if (days > 0)
{
if (terse)
buff << days << ", ";
else
buff << days << (days > 1 ? " days" : "day");
}
buff << std::setw(2) << hours << ':'
<< std::setw(2) << mins << ':'
<< std::setw(2) << secs;
return buff.str();
}
void set_redraw_status(unsigned long flags)
{
you.redraw_status_flags |= flags;
}
static bool _tag_follower_at(const coord_def &pos)
{
if (!in_bounds(pos) || pos == you.pos())
return (false);
monsters *fmenv = monster_at(pos);
if (fmenv == NULL)
return (false);
if (fmenv->type == MONS_PLAYER_GHOST
|| !fmenv->alive()
|| fmenv->incapacitated()
|| !mons_can_use_stairs(fmenv)
|| mons_is_stationary(fmenv))
{
return (false);
}
if (!monster_habitable_grid(fmenv, DNGN_FLOOR))
return (false);
if (fmenv->speed_increment < 50)
return (false);
if (!mons_friendly(fmenv)
&& (!mons_is_seeking(fmenv) || fmenv->foe != MHITYOU))
{
return (false);
}
if ((pos - you.pos()).abs() > 2)
{
if (!mons_friendly(fmenv))
return (false);
if ((you.religion != GOD_YREDELEMNUL && you.religion != GOD_BEOGH)
|| !is_follower(fmenv))
{
return (false);
}
}
fmenv->flags |= MF_TAKING_STAIRS;
fmenv->patrol_point.reset();
fmenv->travel_path.clear();
fmenv->travel_target = MTRAV_NONE;
#if DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "%s is marked for following.",
fmenv->name(DESC_CAP_THE, true).c_str() );
#endif
return (true);
}
static int follower_tag_radius2()
{
for (adjacent_iterator ai; ai; ++ai)
{
if (const monsters *mon = monster_at(*ai))
if (!mons_friendly(mon))
return (2);
}
return (6 * 6);
}
void tag_followers()
{
const int radius2 = follower_tag_radius2();
int n_followers = 18;
std::vector<coord_def> places[2];
int place_set = 0;
places[place_set].push_back(you.pos());
memset(travel_point_distance, 0, sizeof(travel_distance_grid_t));
while (!places[place_set].empty())
{
for (int i = 0, size = places[place_set].size(); i < size; ++i)
{
const coord_def &p = places[place_set][i];
coord_def fp;
for (fp.x = p.x - 1; fp.x <= p.x + 1; ++fp.x)
for (fp.y = p.y - 1; fp.y <= p.y + 1; ++fp.y)
{
if (fp == p || (fp - you.pos()).abs() > radius2
|| !in_bounds(fp) || travel_point_distance[fp.x][fp.y])
{
continue;
}
travel_point_distance[fp.x][fp.y] = 1;
if (_tag_follower_at(fp))
{
if (--n_followers <= 0)
return;
places[!place_set].push_back(fp);
}
}
}
places[place_set].clear();
place_set = !place_set;
}
}
void untag_followers()
{
for (int m = 0; m < MAX_MONSTERS; ++m)
menv[m].flags &= (~MF_TAKING_STAIRS);
}
unsigned char get_ch()
{
mouse_control mc(MOUSE_MODE_MORE);
unsigned char gotched = getch();
if (gotched == 0)
gotched = getch();
return gotched;
}
void cio_init()
{
crawl_state.io_inited = true;
#if defined(UNIX) && !defined(USE_TILE)
unixcurses_startup();
#endif
#if defined(TARGET_OS_WINDOWS) && !defined(USE_TILE)
init_libw32c();
#endif
#ifdef TARGET_OS_DOS
init_libdos();
#endif
crawl_view.init_geometry();
#ifdef USE_TILE
tiles.resize();
#endif
if (Options.char_set == CSET_UNICODE && !crawl_state.unicode_ok)
{
crawl_state.add_startup_error(
"Unicode glyphs are not available, falling back to ASCII.");
Options.char_set = CSET_ASCII;
}
}
void cio_cleanup()
{
if (!crawl_state.io_inited)
return;
#if defined(USE_TILE)
tiles.shutdown();
#elif defined(UNIX)
unixcurses_shutdown();
#endif
#if defined(TARGET_OS_WINDOWS) && !defined(USE_TILE)
deinit_libw32c();
#endif
msg::deinitialise_mpr_streams();
clear_globals_on_exit();
crawl_state.io_inited = false;
}
void clear_globals_on_exit()
{
clear_rays_on_exit();
clear_zap_info_on_exit();
}
void end(int exit_code, bool print_error, const char *format, ...)
{
std::string error = print_error? strerror(errno) : "";
cio_cleanup();
databaseSystemShutdown();
if (format)
{
va_list arg;
va_start(arg, format);
char buffer[1024];
vsnprintf(buffer, sizeof buffer, format, arg);
va_end(arg);
if (error.empty())
error = std::string(buffer);
else
error = std::string(buffer) + ": " + error;
}
if (!error.empty())
{
if (error[error.length() - 1] != '\n')
error += "\n";
fprintf(stderr, "%s", error.c_str());
error.clear();
}
#if (defined(TARGET_OS_WINDOWS) && !defined(USE_TILE)) || \
defined(TARGET_OS_DOS) || \
defined(DGL_PAUSE_AFTER_ERROR)
if (exit_code && !crawl_state.arena
&& !crawl_state.seen_hups && !crawl_state.test)
{
fprintf(stderr, "Hit Enter to continue...\n");
getchar();
}
#endif
exit(exit_code);
}
void redraw_screen(void)
{
if (!crawl_state.need_save)
{
clrscr();
return;
}
draw_border();
you.redraw_hit_points = true;
you.redraw_magic_points = true;
you.redraw_strength = true;
you.redraw_intelligence = true;
you.redraw_dexterity = true;
you.redraw_armour_class = true;
you.redraw_evasion = true;
you.redraw_experience = true;
you.wield_change = true;
you.redraw_quiver = 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);
bool note_status = notes_are_active();
activate_notes(false);
new_level();
#ifdef DGL_SIMPLE_MESSAGING
update_message_status();
#endif
update_turn_count();
activate_notes(note_status);
viewwindow(true, false);
}
int stepdown_value(int base_value, int stepping, int first_step,
int last_step, int ceiling_value)
{
int return_value = base_value;
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; }
if (ceiling_value != -1 && return_value > ceiling_value)
return ceiling_value; else
return return_value;
}
int skill_bump( int skill )
{
return ((you.skills[skill] < 3) ? you.skills[skill] * 2
: you.skills[skill] + 3);
}
int stat_mult( int stat_level, int value, int div, int shift )
{
return (((stat_level + shift) * value) / ((div > 1) ? div : 1));
}
int stat_div( int stat_level, int value, int mult, int shift )
{
int div = stat_level + shift;
if (div < 1)
div = 1;
return ((mult * value) / div);
}
int div_round_up(int num, int den)
{
return (num / den + (num % den != 0));
}
void modify_all_stats(int STmod, int IQmod, int DXmod)
{
if (STmod)
{
you.strength += STmod;
you.max_strength += STmod;
you.redraw_strength = true;
}
if (IQmod)
{
you.intel += IQmod;
you.max_intel += IQmod;
you.redraw_intelligence = true;
}
if (DXmod)
{
you.dex += DXmod;
you.max_dex += DXmod;
you.redraw_dexterity = true;
}
}
void canned_msg(canned_message_type which_message)
{
switch (which_message)
{
case MSG_SOMETHING_APPEARS:
mprf("Something appears %s!",
(you.species == SP_NAGA || player_mutation_level(MUT_HOOVES))
? "before you" : "at your feet");
break;
case MSG_NOTHING_HAPPENS:
mpr("Nothing appears to happen.");
break;
case MSG_YOU_RESIST:
mpr("You resist.");
learned_something_new(TUT_YOU_RESIST);
break;
case MSG_YOU_PARTIALLY_RESIST:
mpr("You partially resist.");
break;
case MSG_TOO_BERSERK:
mpr("You are too berserk!");
crawl_state.cancel_cmd_repeat();
break;
case MSG_PRESENT_FORM:
mpr("You can't do that in your present form.");
crawl_state.cancel_cmd_repeat();
break;
case MSG_NOTHING_CARRIED:
mpr("You aren't carrying anything.");
crawl_state.cancel_cmd_repeat();
break;
case MSG_CANNOT_DO_YET:
mpr("You can't do that yet.");
crawl_state.cancel_cmd_repeat();
break;
case MSG_OK:
mpr("Okay, then.", MSGCH_PROMPT);
crawl_state.cancel_cmd_repeat();
break;
case MSG_UNTHINKING_ACT:
mpr("Why would you want to do that?");
crawl_state.cancel_cmd_repeat();
break;
case MSG_SPELL_FIZZLES:
mpr("The spell fizzles.");
break;
case MSG_HUH:
mpr("Huh?", MSGCH_EXAMINE_FILTER);
crawl_state.cancel_cmd_repeat();
break;
case MSG_EMPTY_HANDED:
mpr("You are now empty-handed.");
break;
}
}
bool yes_or_no( const char* fmt, ... )
{
char buf[200];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof buf, fmt, args);
va_end(args);
buf[sizeof(buf)-1] = 0;
mprf(MSGCH_PROMPT, "%s? (Confirm with \"yes\".) ", buf);
if (cancelable_get_line(buf, sizeof buf))
return (false);
if (strcasecmp(buf, "yes") != 0)
return (false);
return (true);
}
bool yesno( const char *str, bool safe, int safeanswer, bool clear_after,
bool interrupt_delays, bool noprompt,
const explicit_keymap *map )
{
if (interrupt_delays && !crawl_state.is_repeating_cmd())
interrupt_activity( AI_FORCE_INTERRUPT );
std::string prompt = make_stringf("%s ", str ? str : "Buggy prompt?");
while (true)
{
if (!noprompt)
mpr(prompt.c_str(), MSGCH_PROMPT);
int tmp = getchm(KMC_CONFIRM);
#if defined(USE_UNIX_SIGNALS) && defined(SIGHUP_SAVE) && defined(USE_CURSES)
if (crawl_state.seen_hups && !safeanswer)
sighup_save_and_exit();
#endif
if (map && map->find(tmp) != map->end())
tmp = map->find(tmp)->second;
if (safeanswer
&& (tmp == ' ' || tmp == ESCAPE || tmp == CONTROL('G')
|| tmp == '\r' || tmp == '\n' || crawl_state.seen_hups))
{
tmp = safeanswer;
}
if (Options.easy_confirm == CONFIRM_ALL_EASY
|| tmp == safeanswer
|| 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 if (!noprompt)
mpr("[Y]es or [N]o only, please.");
}
}
static std::string _list_alternative_yes(char yes1, char yes2,
bool lowered = false,
bool brackets = false)
{
std::string help = "";
bool print_yes = false;
if (yes1 != 'Y')
{
if (lowered)
help += tolower(yes1);
else
help += yes1;
print_yes = true;
}
if (yes2 != 'Y' && yes2 != yes1)
{
if (print_yes)
help += "/";
if (lowered)
help += tolower(yes2);
else
help += yes2;
print_yes = true;
}
if (print_yes)
{
if (brackets)
help = " (" + help + ")";
else
help = "/" + help;
}
return help;
}
static std::string _list_allowed_keys(char yes1, char yes2,
bool lowered = false,
bool allow_all = false)
{
std::string result = " [";
result += (lowered ? "y" : "Y");
result += _list_alternative_yes(yes1, yes2, lowered);
if (allow_all)
result += (lowered? "/a" : "/A");
result += (lowered ? "/n/q" : "/N/Q");
result += "]";
return (result);
}
int yesnoquit( const char* str, bool safe, int safeanswer, bool allow_all,
bool clear_after, char alt_yes, char alt_yes2 )
{
if (!crawl_state.is_repeating_cmd())
interrupt_activity( AI_FORCE_INTERRUPT );
std::string prompt =
make_stringf("%s%s ", str ? str : "Buggy prompt?",
_list_allowed_keys(alt_yes, alt_yes2,
safe, allow_all).c_str());
while (true)
{
mpr(prompt.c_str(), MSGCH_PROMPT);
int tmp = getchm(KMC_CONFIRM);
if (tmp == CK_ESCAPE || tmp == CONTROL('G') || tmp == 'q' || tmp == 'Q'
|| crawl_state.seen_hups)
{
return -1;
}
if ((tmp == ' ' || tmp == '\r' || tmp == '\n') && safeanswer)
tmp = safeanswer;
if (Options.easy_confirm == CONFIRM_ALL_EASY
|| tmp == safeanswer
|| safe && Options.easy_confirm == CONFIRM_SAFE_EASY)
{
tmp = toupper( tmp );
}
if (clear_after)
mesclr();
if (tmp == 'N')
return 0;
else if (tmp == 'Y' || tmp == alt_yes || tmp == alt_yes2)
return 1;
else if (allow_all)
{
if (tmp == 'A')
return 2;
else
mprf("Choose [Y]es%s, [N]o, [Q]uit, or [A]ll!",
_list_alternative_yes(alt_yes, alt_yes2, false, true).c_str());
}
else
{
mprf("[Y]es%s, [N]o or [Q]uit only, please.",
_list_alternative_yes(alt_yes, alt_yes2, false, true).c_str());
}
}
}
bool silenced(const coord_def& p)
{
return (you.duration[DUR_SILENCE] && distance(p, you.pos()) <= 6*6 + 1);
}
bool player_can_hear(const coord_def& p)
{
return (!silenced(p) && !silenced(you.pos()));
}
char index_to_letter(int the_index)
{
return (the_index + ((the_index < 26) ? 'a' : ('A' - 26)));
}
int letter_to_index(int the_letter)
{
if (the_letter >= 'a' && the_letter <= 'z')
the_letter -= 'a';
else if (the_letter >= 'A' && the_letter <= 'Z')
the_letter -= ('A' - 26);
return (the_letter);
}
int near_stairs(const coord_def &p, int max_dist,
dungeon_char_type &stair_type, branch_type &branch)
{
for (radius_iterator ri(p, max_dist, true, false); ri; ++ri)
{
const dungeon_feature_type feat = grd(*ri);
if (feat_is_stair(feat))
{
if (feat_is_escape_hatch(feat))
continue;
stair_type = get_feature_dchar(feat);
for (int i = 0; i < NUM_BRANCHES; ++i)
{
if (branches[i].entry_stairs == feat)
{
branch = branches[i].id;
break;
}
else if (branches[i].exit_stairs == feat)
{
branch = branches[i].parent_branch;
break;
}
}
return (*ri == you.pos()) ? 2 : 1;
}
}
return (false);
}
bool is_trap_square(dungeon_feature_type grid)
{
return (grid >= DNGN_TRAP_MECHANICAL && grid <= DNGN_UNDISCOVERED_TRAP);
}
void zap_los_monsters()
{
losight(env.show, you.pos());
for (rectangle_iterator ri(crawl_view.vlos1, crawl_view.vlos2); ri; ++ri )
{
if (!in_vlos(*ri))
continue;
const coord_def g = view2grid(*ri);
if (!map_bounds(g))
continue;
if (g == you.pos())
continue;
if (Options.tutorial_events[TUT_SEEN_FIRST_OBJECT])
{
int item = igrd(g);
if (item != NON_ITEM && is_valid_item(mitm[item]) )
destroy_item(item);
}
monsters *mon = monster_at(g);
if (mon == NULL)
continue;
if (mons_class_flag( mon->type, M_NO_EXP_GAIN ))
continue;
#ifdef DEBUG_DIAGNOSTICS
mprf(MSGCH_DIAGNOSTICS, "Dismissing %s",
mon->name(DESC_PLAIN, true).c_str() );
#endif
mon->flags |= MF_HARD_RESET;
monster_die(mon, KILL_DISMISSED, NON_MONSTER, true, true);
}
}
int integer_sqrt(int value)
{
if (value <= 0)
return (value);
int very_old_retval = -1;
int old_retval = 0;
int retval = 1;
while (very_old_retval != old_retval
&& very_old_retval != retval
&& retval != old_retval)
{
very_old_retval = old_retval;
old_retval = retval;
retval = (value / old_retval + old_retval) / 2;
}
return (retval);
}
int random_rod_subtype()
{
return STAFF_FIRST_ROD + random2(NUM_STAVES - STAFF_FIRST_ROD);
}