summaryrefslogtreecommitdiffstats
path: root/stone_soup/crawl-ref/source/files.cc
diff options
context:
space:
mode:
Diffstat (limited to 'stone_soup/crawl-ref/source/files.cc')
-rw-r--r--stone_soup/crawl-ref/source/files.cc2112
1 files changed, 0 insertions, 2112 deletions
diff --git a/stone_soup/crawl-ref/source/files.cc b/stone_soup/crawl-ref/source/files.cc
deleted file mode 100644
index 6c13965702..0000000000
--- a/stone_soup/crawl-ref/source/files.cc
+++ /dev/null
@@ -1,2112 +0,0 @@
-/*
- * File: files.cc
- * Summary: Functions used to save and load levels/games.
- * Written by: Linley Henzell and Alexey Guzeev
- *
- * Modified for Crawl Reference by $Author$ on $Date$
- *
- * Change History (most recent first):
- *
- * <7> 19 June 2000 GDL Change handle to FILE *
- * <6> 11/14/99 cdl Don't let player ghosts follow you up/down
- * <5> 7/13/99 BWR Monsters now regenerate hps off level &
- ghosts teleport
- * <4> 6/13/99 BWR Added tmp file pairs to save file.
- * <3> 6/11/99 DML Replaced temp file deletion code.
- *
- * <2> 5/12/99 BWR Multiuser system support,
- * including appending UID to
- * name, and compressed saves
- * in the SAVE_DIR_PATH directory
- *
- * <1> --/--/-- LRH Created
- */
-
-#include "AppHdr.h"
-#include "files.h"
-#include "version.h"
-
-#include <string.h>
-#include <string>
-#include <stdlib.h>
-#include <stdio.h>
-#include <ctype.h>
-
-#ifdef DOS
-#include <conio.h>
-#include <file.h>
-#endif
-
-#ifdef UNIX
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#endif
-
-#ifdef USE_EMX
-#include <sys/types.h>
-#include <fcntl.h>
-#include <unistd.h>
-#endif
-
-#ifdef OS9
-#include <stat.h>
-#else
-#include <sys/stat.h>
-#endif
-
-#ifdef __MINGW32__
-#include <io.h>
-#endif
-
-#include "externs.h"
-
-#include "cloud.h"
-#include "clua.h"
-#include "debug.h"
-#include "dungeon.h"
-#include "itemname.h"
-#include "itemprop.h"
-#include "items.h"
-#include "message.h"
-#include "misc.h"
-#include "monstuff.h"
-#include "mon-util.h"
-#include "mstuff2.h"
-#include "player.h"
-#include "randart.h"
-#include "skills2.h"
-#include "stash.h"
-#include "stuff.h"
-#include "tags.h"
-#include "travel.h"
-#include "wpn-misc.h"
-
-void save_level(int level_saved, bool was_a_labyrinth, char where_were_you);
-
-// temp file pairs used for file level cleanup
-extern FixedArray < bool, MAX_LEVELS, MAX_BRANCHES > tmp_file_pairs;
-
-/*
- Order for looking for conjurations for the 1st & 2nd spell slots,
- when finding spells to be remembered by a player's ghost:
- */
-unsigned char search_order_conj[] = {
-/* 0 */
- SPELL_LEHUDIBS_CRYSTAL_SPEAR,
- SPELL_BOLT_OF_DRAINING,
- SPELL_AGONY,
- SPELL_DISINTEGRATE,
- SPELL_LIGHTNING_BOLT,
- SPELL_STICKY_FLAME,
- SPELL_ISKENDERUNS_MYSTIC_BLAST,
- SPELL_BOLT_OF_FIRE,
- SPELL_BOLT_OF_COLD,
- SPELL_FIREBALL,
- SPELL_DELAYED_FIREBALL,
-/* 10 */
- SPELL_VENOM_BOLT,
- SPELL_BOLT_OF_IRON,
- SPELL_STONE_ARROW,
- SPELL_THROW_FLAME,
- SPELL_THROW_FROST,
- SPELL_PAIN,
- SPELL_STING,
- SPELL_MAGIC_DART,
- SPELL_NO_SPELL, // end search
-};
-
-/*
- Order for looking for summonings and self-enchants for the 3rd spell slot:
- */
-unsigned char search_order_third[] = {
-/* 0 */
- SPELL_SYMBOL_OF_TORMENT,
- SPELL_SUMMON_GREATER_DEMON,
- SPELL_SUMMON_WRAITHS,
- SPELL_SUMMON_HORRIBLE_THINGS,
- SPELL_SUMMON_DEMON,
- SPELL_DEMONIC_HORDE,
- SPELL_HASTE,
- SPELL_ANIMATE_DEAD,
- SPELL_INVISIBILITY,
- SPELL_CALL_IMP,
- SPELL_SUMMON_SMALL_MAMMAL,
-/* 10 */
- SPELL_CONTROLLED_BLINK,
- SPELL_BLINK,
- SPELL_NO_SPELL, // end search
-};
-
-/*
- Order for looking for enchants for the 4th + 5th spell slot. If fails, will
- go through conjs.
- Note: Dig must be in misc2 (5th) position to work.
- */
-unsigned char search_order_misc[] = {
-/* 0 */
- SPELL_AGONY,
- SPELL_BANISHMENT,
- SPELL_PARALYZE,
- SPELL_CONFUSE,
- SPELL_SLOW,
- SPELL_POLYMORPH_OTHER,
- SPELL_TELEPORT_OTHER,
- SPELL_DIG,
- SPELL_NO_SPELL, // end search
-};
-
-/* Last slot (emergency) can only be teleport self or blink. */
-
-static void redraw_all(void)
-{
- 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_experience = 1;
- you.redraw_gold = 1;
-
- you.redraw_status_flags = REDRAW_LINE_1_MASK | REDRAW_LINE_2_MASK | REDRAW_LINE_3_MASK;
-}
-
-struct ghost_struct ghost;
-
-unsigned char translate_spell(unsigned char spel);
-unsigned char search_third_list(unsigned char ignore_spell);
-unsigned char search_second_list(unsigned char ignore_spell);
-unsigned char search_first_list(unsigned char ignore_spell);
-
-void add_spells( struct ghost_struct &gs );
-void generate_random_demon();
-
-static bool determine_version( FILE *restoreFile,
- char &majorVersion, char &minorVersion );
-
-static void restore_version( FILE *restoreFile,
- char majorVersion, char minorVersion );
-
-static bool determine_level_version( FILE *levelFile,
- char &majorVersion, char &minorVersion );
-
-static void restore_level_version( FILE *levelFile,
- char majorVersion, char minorVersion );
-
-static bool determine_ghost_version( FILE *ghostFile,
- char &majorVersion, char &minorVersion );
-
-static void restore_ghost_version( FILE *ghostFile,
- char majorVersion, char minorVersion );
-
-static void restore_tagged_file( FILE *restoreFile, int fileType,
- char minorVersion );
-
-static void load_ghost();
-
-std::string get_savedir_filename(const char *prefix, const char *suffix,
- const char *extension)
-{
- char filename[1200];
-#ifdef SAVE_DIR_PATH
- snprintf(filename, sizeof filename, SAVE_DIR_PATH "%s%d%s.%s",
- prefix, (int) getuid(), suffix, extension);
-#else
- snprintf(filename, sizeof filename, "%s%s.%s",
- prefix, suffix, extension);
-#ifdef DOS
- strupr(filename);
-#endif
-#endif
- return std::string(filename);
-}
-
-std::string get_prefs_filename()
-{
- return get_savedir_filename("start", "ns", "prf");
-}
-
-void make_filename( char *buf, const char *prefix, int level, int where,
- bool isLabyrinth, bool isGhost )
-{
- UNUSED( isGhost );
-
- char suffix[4], lvl[5];
- char finalprefix[kFileNameLen];
-
- strcpy(suffix, (level < 10) ? "0" : "");
- itoa(level, lvl, 10);
- strcat(suffix, lvl);
- suffix[2] = where + 97;
- suffix[3] = '\0';
-
- // init buf
- buf[0] = '\0';
-
-#ifdef SAVE_DIR_PATH
- strcpy(buf, SAVE_DIR_PATH);
-#endif
-
- strncpy(finalprefix, prefix, kFileNameLen);
- finalprefix[kFileNameLen] = '\0';
-
- strcat(buf, finalprefix);
-
-#ifdef SAVE_DIR_PATH
- // everyone sees everyone else's ghosts. :)
- char uid[10];
- if (!isGhost)
- {
- itoa( (int) getuid(), uid, 10 );
- strcat(buf, uid);
- }
-#endif
-
- strcat(buf, ".");
- if (isLabyrinth)
- strcat(buf, "lab"); // temporary level
- else
- strcat(buf, suffix);
-}
-
-static void write_tagged_file( FILE *dataFile, char majorVersion,
- char minorVersion, int fileType )
-{
- struct tagHeader th;
-
- // find all relevant tags
- char tags[NUM_TAGS];
- tag_set_expected(tags, fileType);
-
- // write version
- struct tagHeader versionTag;
- versionTag.offset = 0;
- versionTag.tagID = TAG_VERSION;
- marshallByte(versionTag, majorVersion);
- marshallByte(versionTag, minorVersion);
- tag_write(versionTag, dataFile);
-
- // all other tags
- for(int i=1; i<NUM_TAGS; i++)
- {
- if (tags[i] == 1)
- {
- tag_construct(th, i);
- tag_write(th, dataFile);
- }
- }
-}
-
-bool travel_load_map( char branch, int absdepth )
-{
- char cha_fil[kFileNameSize];
-
- make_filename( cha_fil, you.your_name, absdepth, branch,
- false, false );
-#ifdef DOS
- strupr(cha_fil);
-#endif
-
- // Try to open level savefile.
- FILE *levelFile = fopen(cha_fil, "rb");
- if (!levelFile)
- return false;
-
- // BEGIN -- must load the old level : pre-load tasks
-
- // LOAD various tags
- char majorVersion;
- char minorVersion;
-
- if (!determine_level_version( levelFile, majorVersion, minorVersion )
- || majorVersion != SAVE_MAJOR_VERSION)
- {
- fclose(levelFile);
- return false;
- }
-
- tag_read(levelFile, minorVersion);
-
- fclose( levelFile );
-
- return true;
-}
-
-void load( unsigned char stair_taken, int load_mode, bool was_a_labyrinth,
- char old_level, char where_were_you2 )
-{
- int j = 0;
- int i = 0, count_x = 0, count_y = 0;
- char cha_fil[kFileNameSize];
-
- int foll_class[8];
- int foll_hp[8];
- int foll_hp_max[8];
- unsigned char foll_HD[8];
- int foll_AC[8];
- char foll_ev[8];
- unsigned char foll_speed[8];
- unsigned char foll_speed_inc[8];
-
- unsigned char foll_targ_1_x[8];
- unsigned char foll_targ_1_y[8];
- unsigned char foll_beh[8];
- unsigned char foll_att[8];
- int foll_sec[8];
- unsigned char foll_hit[8];
-
- unsigned char foll_ench[8][NUM_MON_ENCHANTS];
- unsigned char foll_flags[8];
-
- item_def foll_item[8][8];
-
- int itmf = 0;
- int ic = 0;
- int imn = 0;
- int val;
-
- bool just_created_level = false;
-
-#ifdef DOS_TERM
- window(1, 1, 80, 25);
-#endif
-
- make_filename( cha_fil, you.your_name, you.your_level, you.where_are_you,
- you.level_type != LEVEL_DUNGEON, false );
-
- if (you.level_type == LEVEL_DUNGEON)
- {
- if (tmp_file_pairs[you.your_level][you.where_are_you] == false)
- {
- // make sure old file is gone
- unlink(cha_fil);
-
- // save the information for later deletion -- DML 6/11/99
- tmp_file_pairs[you.your_level][you.where_are_you] = true;
- }
- }
-
- you.prev_targ = MHITNOT;
-
- int following = -1;
- int minvc = 0;
-
- // Don't delete clouds just because the player saved and restarted.
- if (load_mode != LOAD_RESTART_GAME)
- {
- for (int clouty = 0; clouty < MAX_CLOUDS; ++clouty)
- delete_cloud( clouty );
-
- ASSERT( env.cloud_no == 0 );
- }
-
- // This block is to grab followers and save the old level to disk.
- if (load_mode == LOAD_ENTER_LEVEL)
- {
- // grab followers
- for (count_x = you.x_pos - 1; count_x < you.x_pos + 2; count_x++)
- {
- for (count_y = you.y_pos - 1; count_y < you.y_pos + 2; count_y++)
- {
- if (count_x == you.x_pos && count_y == you.y_pos)
- continue;
-
- following++;
- foll_class[following] = -1;
-
- if (mgrd[count_x][count_y] == NON_MONSTER)
- continue;
-
- struct monsters *fmenv = &menv[mgrd[count_x][count_y]];
-
- if (fmenv->type == MONS_PLAYER_GHOST
- && fmenv->hit_points < fmenv->max_hit_points / 2)
- {
- mpr("The ghost fades into the shadows.");
- monster_teleport(fmenv, true);
- continue;
- }
-
- // monster has to be already tagged in order to follow:
- if (!testbits( fmenv->flags, MF_TAKING_STAIRS ))
- continue;
-
-#if DEBUG_DIAGNOSTICS
- snprintf( info, INFO_SIZE, "%s is following.",
- ptr_monam( fmenv, DESC_CAP_THE ) );
- mpr( info, MSGCH_DIAGNOSTICS );
-#endif
-
- foll_class[following] = fmenv->type;
- foll_hp[following] = fmenv->hit_points;
- foll_hp_max[following] = fmenv->max_hit_points;
- foll_HD[following] = fmenv->hit_dice;
- foll_AC[following] = fmenv->armour_class;
- foll_ev[following] = fmenv->evasion;
- foll_speed[following] = fmenv->speed;
- foll_speed_inc[following] = fmenv->speed_increment;
- foll_targ_1_x[following] = fmenv->target_x;
- foll_targ_1_y[following] = fmenv->target_y;
-
- for (minvc = 0; minvc < NUM_MONSTER_SLOTS; ++minvc)
- {
- const int item = fmenv->inv[minvc];
- if (item == NON_ITEM)
- {
- foll_item[following][minvc].quantity = 0;
- continue;
- }
-
- foll_item[following][minvc] = mitm[item];
- destroy_item( item );
- }
-
- foll_beh[following] = fmenv->behaviour;
- foll_att[following] = fmenv->attitude;
- foll_sec[following] = fmenv->number;
- foll_hit[following] = fmenv->foe;
-
- for (j = 0; j < NUM_MON_ENCHANTS; j++)
- {
- foll_ench[following][j] = fmenv->enchantment[j];
- fmenv->enchantment[j] = ENCH_NONE;
- }
-
- foll_flags[following] = fmenv->flags;
-
- fmenv->flags = 0;
- fmenv->type = -1;
- fmenv->hit_points = 0;
- fmenv->max_hit_points = 0;
- fmenv->hit_dice = 0;
- fmenv->armour_class = 0;
- fmenv->evasion = 0;
-
- mgrd[count_x][count_y] = NON_MONSTER;
- }
- } // end of grabbing followers
-
- if (!was_a_labyrinth)
- save_level( old_level, false, where_were_you2 );
-
- was_a_labyrinth = false;
- }
-
- // clear out ghost/demon lord information:
- strcpy( ghost.name, "" );
- for (ic = 0; ic < NUM_GHOST_VALUES; ++ic)
- ghost.values[ic] = 0;
-
-#ifdef DOS
- strupr(cha_fil);
-#endif
-
- // Try to open level savefile.
- FILE *levelFile = fopen(cha_fil, "rb");
-
- // GENERATE new level when the file can't be opened:
- if (levelFile == NULL)
- {
- strcpy(ghost.name, "");
-
- for (imn = 0; imn < NUM_GHOST_VALUES; ++imn)
- ghost.values[imn] = 0;
-
- builder( you.your_level, you.level_type );
- just_created_level = true;
-
- if (you.level_type == LEVEL_PANDEMONIUM)
- generate_random_demon();
-
- if (you.your_level > 1 && one_chance_in(3))
- load_ghost();
- }
- else
- {
- // BEGIN -- must load the old level : pre-load tasks
-
- // LOAD various tags
- char majorVersion = 0;
- char minorVersion = 0;
-
- if (!determine_level_version( levelFile, majorVersion, minorVersion ))
- {
- perror("\nLevel file appears to be invalid.\n");
- end(-1);
- }
-
- restore_level_version( levelFile, majorVersion, minorVersion );
-
- // sanity check - EOF
- if (!feof( levelFile ))
- {
- snprintf( info, INFO_SIZE, "\nIncomplete read of \"%s\" - aborting.\n", cha_fil);
- perror(info);
- end(-1);
- }
-
- fclose( levelFile );
-
- // POST-LOAD tasks :
- link_items();
- redraw_all();
- }
-
- // closes all the gates if you're on the way out
- for (i = 0; i < GXM; i++)
- {
- for (j = 0; j < GYM; j++)
- {
- if (just_created_level)
- env.map[i][j] = 0;
-
- if (you.char_direction == DIR_ASCENDING
- && you.level_type != LEVEL_PANDEMONIUM)
- {
- if (grd[i][j] == DNGN_ENTER_HELL
- || grd[i][j] == DNGN_ENTER_ABYSS
- || grd[i][j] == DNGN_ENTER_PANDEMONIUM)
- {
- grd[i][j] = DNGN_STONE_ARCH;
- }
- }
-
- if (load_mode != LOAD_RESTART_GAME)
- env.cgrid[i][j] = EMPTY_CLOUD;
- }
- }
-
- // This next block is for cases where we want to look for a stairs
- // to place the player.
- if (load_mode != LOAD_RESTART_GAME && you.level_type != LEVEL_ABYSS)
- {
- bool find_first = true;
-
- // Order is important here:
- if (you.level_type == LEVEL_DUNGEON
- && where_were_you2 == BRANCH_VESTIBULE_OF_HELL
- && stair_taken == DNGN_STONE_STAIRS_UP_I)
- {
- // leaving hell - look for entry potal first
- stair_taken = DNGN_ENTER_HELL;
- find_first = false;
- }
- else if (stair_taken == DNGN_EXIT_PANDEMONIUM)
- {
- stair_taken = DNGN_ENTER_PANDEMONIUM;
- find_first = false;
- }
- else if (stair_taken == DNGN_EXIT_ABYSS)
- {
- stair_taken = DNGN_ENTER_ABYSS;
- find_first = false;
- }
- else if (stair_taken == DNGN_ENTER_HELL
- || stair_taken == DNGN_ENTER_LABYRINTH)
- {
- // the vestibule and labyrith always start from this stair
- stair_taken = DNGN_STONE_STAIRS_UP_I;
- }
- else if (stair_taken >= DNGN_STONE_STAIRS_DOWN_I
- && stair_taken <= DNGN_ROCK_STAIRS_DOWN)
- {
- // look for coresponding up stair
- stair_taken += (DNGN_STONE_STAIRS_UP_I - DNGN_STONE_STAIRS_DOWN_I);
- }
- else if (stair_taken >= DNGN_STONE_STAIRS_UP_I
- && stair_taken <= DNGN_ROCK_STAIRS_UP)
- {
- // look for coresponding down stair
- stair_taken += (DNGN_STONE_STAIRS_DOWN_I - DNGN_STONE_STAIRS_UP_I);
- }
- else if (stair_taken >= DNGN_RETURN_FROM_ORCISH_MINES
- && stair_taken < 150) // 20 slots reserved
- {
- // find entry point to subdungeon when leaving
- stair_taken += (DNGN_ENTER_ORCISH_MINES - DNGN_RETURN_FROM_ORCISH_MINES);
- }
- else if (stair_taken >= DNGN_ENTER_ORCISH_MINES
- && stair_taken < DNGN_RETURN_FROM_ORCISH_MINES)
- {
- // find exit staircase from subdungeon when entering
- stair_taken += (DNGN_RETURN_FROM_ORCISH_MINES - DNGN_ENTER_ORCISH_MINES);
- }
- else if (stair_taken >= DNGN_ENTER_DIS
- && stair_taken <= DNGN_TRANSIT_PANDEMONIUM)
- {
- // when entering a hell or pandemonium
- stair_taken = DNGN_STONE_STAIRS_UP_I;
- }
- else // Note: stair_taken can equal things like DNGN_FLOOR
- {
- // just find a nice empty square
- stair_taken = DNGN_FLOOR;
- find_first = false;
- }
-
- int found = 0;
- int x_pos = 0, y_pos = 0;
-
- // Start by looking for the expected entry point:
- for (count_x = 0; count_x < GXM; count_x++)
- {
- for (count_y = 0; count_y < GYM; count_y++)
- {
- if (grd[count_x][count_y] == stair_taken)
- {
- found++;
- if (one_chance_in( found ))
- {
- x_pos = count_x;
- y_pos = count_y;
- }
-
- if (find_first)
- goto found_stair; // double break
- }
- }
- }
-
-found_stair:
- if (!found)
- {
- // See if we can find a stairway in the "right" direction:
- for (count_x = 0; count_x < GXM; count_x++)
- {
- for (count_y = 0; count_y < GYM; count_y++)
- {
- if (stair_taken <= DNGN_ROCK_STAIRS_DOWN)
- {
- // looking for any down stairs
- if (grd[count_x][count_y] >= DNGN_STONE_STAIRS_DOWN_I
- && grd[count_x][count_y] <= DNGN_ROCK_STAIRS_DOWN)
- {
- found++;
- if (one_chance_in( found ))
- {
- x_pos = count_x;
- y_pos = count_y;
- }
- }
- }
- else
- {
- // looking for any up stairs
- if (grd[count_x][count_y] >= DNGN_STONE_STAIRS_UP_I
- && grd[count_x][count_y] <= DNGN_ROCK_STAIRS_UP)
- {
- found++;
- if (one_chance_in( found ))
- {
- x_pos = count_x;
- y_pos = count_y;
- }
- }
- }
- }
- }
-
- if (!found)
- {
- // Still not found? Look for any clear terrain:
- for (count_x = 0; count_x < GXM; count_x++)
- {
- for (count_y = 0; count_y < GYM; count_y++)
- {
- if (grd[count_x][count_y] >= DNGN_FLOOR)
- {
- found++;
- if (one_chance_in( found ))
- {
- x_pos = count_x;
- y_pos = count_y;
- }
- }
- }
- }
- }
- }
-
- // If still not found, the level is very buggy.
- ASSERT( found );
-
- you.x_pos = x_pos;
- you.y_pos = y_pos;
- }
- else if (load_mode != LOAD_RESTART_GAME && you.level_type == LEVEL_ABYSS)
- {
- you.x_pos = 45;
- you.y_pos = 35;
- }
-
- // This should fix the "monster occuring under the player" bug?
- if (mgrd[you.x_pos][you.y_pos] != NON_MONSTER)
- monster_teleport(&menv[mgrd[you.x_pos][you.y_pos]], true);
- /*
- if (you.level_type == LEVEL_LABYRINTH || you.level_type == LEVEL_ABYSS)
- grd[you.x_pos][you.y_pos] = DNGN_FLOOR;
- */
- following = 0;
- int fmenv = -1;
-
- // actually "move" the followers if applicable
- if ((you.level_type == LEVEL_DUNGEON
- || you.level_type == LEVEL_PANDEMONIUM)
- && load_mode == LOAD_ENTER_LEVEL)
- {
- for (ic = 0; ic < 2; ic++)
- {
- for (count_x = you.x_pos - 6; count_x < you.x_pos + 7;
- count_x++)
- {
- for (count_y = you.y_pos - 6; count_y < you.y_pos + 7;
- count_y++)
- {
- if (ic == 0
- && ((count_x < you.x_pos - 1)
- || (count_x > you.x_pos + 1)
- || (count_y < you.y_pos - 1)
- || (count_y > you.y_pos + 1)))
- {
- continue;
- }
-
- if (count_x == you.x_pos && count_y == you.y_pos)
- continue;
-
- if (mgrd[count_x][count_y] != NON_MONSTER
- || grd[count_x][count_y] < DNGN_FLOOR)
- {
- continue;
- }
-
- while (menv[following].type != -1)
- {
- following++;
-
- if (following >= MAX_MONSTERS)
- goto out_of_foll;
- }
-
- while (fmenv < 7)
- {
- fmenv++;
-
- if (foll_class[fmenv] == -1)
- continue;
-
- menv[following].type = foll_class[fmenv];
- menv[following].hit_points = foll_hp[fmenv];
- menv[following].max_hit_points = foll_hp_max[fmenv];
- menv[following].hit_dice = foll_HD[fmenv];
- menv[following].armour_class = foll_AC[fmenv];
- menv[following].evasion = foll_ev[fmenv];
- menv[following].speed = foll_speed[fmenv];
- menv[following].x = count_x;
- menv[following].y = count_y;
- menv[following].target_x = 0;
- menv[following].target_y = 0;
- menv[following].speed_increment = foll_speed_inc[fmenv];
-
- for (minvc = 0; minvc < NUM_MONSTER_SLOTS; minvc++)
- {
-
- if (!is_valid_item(foll_item[fmenv][minvc]))
- {
- menv[following].inv[minvc] = NON_ITEM;
- continue;
- }
-
- itmf = get_item_slot(0);
- if (itmf == NON_ITEM)
- {
- menv[following].inv[minvc] = NON_ITEM;
- continue;
- }
-
- mitm[itmf] = foll_item[fmenv][minvc];
- mitm[itmf].x = 0;
- mitm[itmf].y = 0;
- mitm[itmf].link = NON_ITEM;
-
- menv[following].inv[minvc] = itmf;
- }
-
- menv[following].behaviour = foll_beh[fmenv];
- menv[following].attitude = foll_att[fmenv];
- menv[following].number = foll_sec[fmenv];
- menv[following].foe = foll_hit[fmenv];
-
- for (j = 0; j < NUM_MON_ENCHANTS; j++)
- menv[following].enchantment[j]=foll_ench[fmenv][j];
-
- menv[following].flags = foll_flags[fmenv];
- menv[following].flags |= MF_JUST_SUMMONED;
-
- mgrd[count_x][count_y] = following;
- break;
- }
- }
- }
- }
- } // end of moving followers
-
- out_of_foll:
- redraw_all();
-
- // Sanity forcing of monster inventory items (required?)
- for (i = 0; i < MAX_MONSTERS; i++)
- {
- if (menv[i].type == -1)
- continue;
-
- for (j = 0; j < NUM_MONSTER_SLOTS; j++)
- {
- if (menv[i].inv[j] == NON_ITEM)
- continue;
-
- /* items carried by monsters shouldn't be linked */
- if (mitm[menv[i].inv[j]].link != NON_ITEM)
- mitm[menv[i].inv[j]].link = NON_ITEM;
- }
- }
-
- // Translate stairs for pandemonium levels:
- if (you.level_type == LEVEL_PANDEMONIUM)
- {
- for (count_x = 0; count_x < GXM; count_x++)
- {
- for (count_y = 0; count_y < GYM; count_y++)
- {
- if (grd[count_x][count_y] >= DNGN_STONE_STAIRS_UP_I
- && grd[count_x][count_y] <= DNGN_ROCK_STAIRS_UP)
- {
- if (one_chance_in( you.mutation[MUT_PANDEMONIUM] ? 5 : 50 ))
- grd[count_x][count_y] = DNGN_EXIT_PANDEMONIUM;
- else
- grd[count_x][count_y] = DNGN_FLOOR;
- }
-
- if (grd[count_x][count_y] >= DNGN_ENTER_LABYRINTH
- && grd[count_x][count_y] <= DNGN_ROCK_STAIRS_DOWN)
- {
- grd[count_x][count_y] = DNGN_TRANSIT_PANDEMONIUM;
- }
- }
- }
- }
-
- // Things to update for player entering level
- if (load_mode == LOAD_ENTER_LEVEL)
- {
- // update corpses and fountains
- if (env.elapsed_time != 0.0)
- update_level( you.elapsed_time - env.elapsed_time );
-
- // Centaurs have difficulty with stairs
- val = ((you.species != SP_CENTAUR) ? player_movement_speed() : 15);
-
- // new levels have less wary monsters:
- if (just_created_level)
- val /= 2;
-
- val -= (stepdown_value( check_stealth(), 50, 50, 150, 150 ) / 10);
-
-#if DEBUG_DIAGNOSTICS
- snprintf( info, INFO_SIZE, "arrival time: %d", val );
- mpr( info, MSGCH_DIAGNOSTICS );
-#endif
-
- if (val > 0)
- {
- you.time_taken = val;
- handle_monsters();
- }
- }
-
- // Save the created/updated level out to disk:
- save_level( you.your_level, (you.level_type != LEVEL_DUNGEON),
- you.where_are_you );
-} // end load()
-
-void save_level(int level_saved, bool was_a_labyrinth, char where_were_you)
-{
- char cha_fil[kFileNameSize];
-
- make_filename( cha_fil, you.your_name, level_saved, where_were_you,
- was_a_labyrinth, false );
-
- you.prev_targ = MHITNOT;
-
-#ifdef DOS
- strupr(cha_fil);
-#endif
-
- FILE *saveFile = fopen(cha_fil, "wb");
-
- if (saveFile == NULL)
- {
- strcpy(info, "Unable to open \"");
- strcat(info, cha_fil );
- strcat(info, "\" for writing!");
- perror(info);
- end(-1);
- }
-
- // nail all items to the ground
- fix_item_coordinates();
-
- // 4.0 initial genesis of saved format
- // 4.1 added attitude tag
- // 4.2 replaced old 'enchantment1' and with 'flags' (bitfield)
- // 4.3 changes to make the item structure more sane
- // 4.4 changes to the ghost save section
- // 4.5 spell and ability letter arrays
- // 4.6 inventory slots of items
- // 4.7 origin tracking for items
- // 4.8 widened env.map to 2 bytes
-
- // [dshaligram] Winding major version back all the way to 0.
- write_tagged_file( saveFile, SAVE_MAJOR_VERSION, 8, TAGTYPE_LEVEL );
-
- fclose(saveFile);
-
-#ifdef SHARED_FILES_CHMOD_PRIVATE
- chmod(cha_fil, SHARED_FILES_CHMOD_PRIVATE);
-#endif
-} // end save_level()
-
-void save_game(bool leave_game)
-{
- char charFile[kFileNameSize];
-#ifdef STASH_TRACKING
- char stashFile[kFileNameSize + 4];
-#endif
- char killFile[kFileNameSize + 4];
- char travelCacheFile[kFileNameSize + 4];
-#ifdef CLUA_BINDINGS
- char luaFile[kFileNameSize + 4];
-#endif
-
-#ifdef SAVE_PACKAGE_CMD
- char cmd_buff[1024];
- char name_buff[kFileNameSize];
-
- snprintf( name_buff, sizeof(name_buff),
- SAVE_DIR_PATH "%s%d", you.your_name, (int) getuid() );
-
- snprintf( cmd_buff, sizeof(cmd_buff),
- SAVE_PACKAGE_CMD, name_buff, name_buff );
-
-#ifdef STASH_TRACKING
- strcpy(stashFile, name_buff);
-#endif
-#ifdef CLUA_BINDINGS
- strcpy(luaFile, name_buff);
-#endif
- strcpy(killFile, name_buff);
- strcpy(travelCacheFile, name_buff);
- snprintf( charFile, sizeof(charFile),
- "%s.sav", name_buff );
-
-#else
- strncpy(charFile, you.your_name, kFileNameLen);
- charFile[kFileNameLen] = 0;
-
-#ifdef STASH_TRACKING
- strcpy(stashFile, charFile);
-#endif
-#ifdef CLUA_BINDINGS
- strcpy(luaFile, charFile);
-#endif
- strcpy(killFile, charFile);
- strcpy(travelCacheFile, charFile);
- strcat(charFile, ".sav");
-
-#ifdef DOS
- strupr(charFile);
-#ifdef STASH_TRACKING
- strupr(stashFile);
-#endif
-#ifdef CLUA_BINDINGS
- strupr(luaFile);
-#endif
- strupr(killFile);
- strupr(travelCacheFile);
-#endif
-#endif
-
-#ifdef STASH_TRACKING
- strcat(stashFile, ".st");
-#endif
-#ifdef CLUA_BINDINGS
- strcat(luaFile, ".lua");
-#endif
- strcat(killFile, ".kil");
- strcat(travelCacheFile, ".tc");
-
-#ifdef STASH_TRACKING
- FILE *stashf = fopen(stashFile, "wb");
- if (stashf)
- {
- stashes.save(stashf);
- fclose(stashf);
-
-#ifdef SHARED_FILES_CHMOD_PRIVATE
- // change mode (unices)
- chmod(stashFile, SHARED_FILES_CHMOD_PRIVATE);
-#endif
- }
-#endif // STASH_TRACKING
-
-#ifdef CLUA_BINDINGS
- clua.save(luaFile);
-#ifdef SHARED_FILES_CHMOD_PRIVATE
- // change mode; note that luaFile may not exist
- chmod(luaFile, SHARED_FILES_CHMOD_PRIVATE);
-#endif
-#endif // CLUA_BINDINGS
-
- FILE *travelf = fopen(travelCacheFile, "wb");
- if (travelf)
- {
- travel_cache.save(travelf);
- fclose(travelf);
-#ifdef SHARED_FILES_CHMOD_PRIVATE
- // change mode (unices)
- chmod(travelCacheFile, SHARED_FILES_CHMOD_PRIVATE);
-#endif
- }
-
- FILE *killf = fopen(killFile, "wb");
- if (killf)
- {
- you.kills.save(killf);
- fclose(killf);
-
-#ifdef SHARED_FILES_CHMOD_PRIVATE
- // change mode (unices)
- chmod(killFile, SHARED_FILES_CHMOD_PRIVATE);
-#endif
- }
-
- FILE *saveFile = fopen(charFile, "wb");
-
- if (saveFile == NULL)
- {
- strcpy(info, "Unable to open \"");
- strcat(info, charFile );
- strcat(info, "\" for writing!");
- perror(info);
- end(-1);
- }
-
- // 4.0 initial genesis of saved format
- // 4.1 changes to make the item structure more sane
- // 4.2 spell and ability tables
- // 4.3 added you.magic_contamination (05/03/05)
- // 4.4 added item origins
- // 4.5 added num_gifts
-
- write_tagged_file( saveFile, SAVE_MAJOR_VERSION, 5, TAGTYPE_PLAYER );
-
- fclose(saveFile);
-
-#ifdef SHARED_FILES_CHMOD_PRIVATE
- // change mode (unices)
- chmod(charFile, SHARED_FILES_CHMOD_PRIVATE);
-#endif
-
- // if just save, early out
- if (!leave_game)
- return;
-
- // must be exiting -- save level & goodbye!
- save_level(you.your_level, (you.level_type != LEVEL_DUNGEON),
- you.where_are_you);
-
-#ifdef DOS_TERM
- window(1, 1, 80, 25);
-#endif
-
- clrscr();
-
-#ifdef SAVE_PACKAGE_CMD
- if (system( cmd_buff ) != 0)
- {
- cprintf( EOL "Warning: Zip command (SAVE_PACKAGE_CMD) returned non-zero value!" EOL );
- }
-
-#ifdef SHARED_FILES_CHMOD_PRIVATE
- strcat( name_buff, PACKAGE_SUFFIX );
- // change mode (unices)
- chmod( name_buff, SHARED_FILES_CHMOD_PRIVATE );
-#endif
-
-#endif
-
- cprintf( "See you soon, %s!" EOL , you.your_name );
-
- end(0);
-} // end save_game()
-
-void load_ghost(void)
-{
- char majorVersion;
- char minorVersion;
- char cha_fil[kFileNameSize];
- int imn;
- int i;
-
- make_filename( cha_fil, "bones", you.your_level, you.where_are_you,
- (you.level_type != LEVEL_DUNGEON), true );
-
- FILE *gfile = fopen(cha_fil, "rb");
-
- if (gfile == NULL)
- return; // no such ghost.
-
- if (!determine_ghost_version(gfile, majorVersion, minorVersion))
- {
- fclose(gfile);
-#if DEBUG_DIAGNOSTICS
- snprintf( info, INFO_SIZE, "Ghost file \"%s\" seems to be invalid.",
- cha_fil);
- mpr( info, MSGCH_DIAGNOSTICS );
- more();
-#endif
- return;
- }
-
- restore_ghost_version(gfile, majorVersion, minorVersion);
-
- // sanity check - EOF
- if (!feof(gfile))
- {
- fclose(gfile);
-#if DEBUG_DIAGNOSTICS
- snprintf( info, INFO_SIZE, "Incomplete read of \"%s\".", cha_fil);
- mpr( info, MSGCH_DIAGNOSTICS );
- more();
-#endif
- return;
- }
-
- fclose(gfile);
-
-#if DEBUG_DIAGNOSTICS
- mpr( "Loaded ghost.", MSGCH_DIAGNOSTICS );
-#endif
-
- // remove bones file - ghosts are hardly permanent.
- unlink(cha_fil);
-
- // translate ghost to monster and place.
- for (imn = 0; imn < MAX_MONSTERS - 10; imn++)
- {
- if (menv[imn].type != -1)
- continue;
-
- menv[imn].type = MONS_PLAYER_GHOST;
- menv[imn].hit_dice = ghost.values[ GVAL_EXP_LEVEL ];
- menv[imn].hit_points = ghost.values[ GVAL_MAX_HP ];
- menv[imn].max_hit_points = ghost.values[ GVAL_MAX_HP ];
- menv[imn].armour_class = ghost.values[ GVAL_AC];
- menv[imn].evasion = ghost.values[ GVAL_EV ];
- menv[imn].speed = 10;
- menv[imn].speed_increment = 70;
- menv[imn].attitude = ATT_HOSTILE;
- menv[imn].behaviour = BEH_WANDER;
- menv[imn].flags = 0;
- menv[imn].foe = MHITNOT;
- menv[imn].foe_memory = 0;
-
- menv[imn].number = 250;
-
- for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
- {
- if (ghost.values[i] != MS_NO_SPELL)
- {
- menv[imn].number = MST_GHOST;
- break;
- }
- }
-
- for (i = 0; i < NUM_MONSTER_SLOTS; i++)
- menv[imn].inv[i] = NON_ITEM;
-
- for (i = 0; i < NUM_MON_ENCHANTS; i++)
- menv[imn].enchantment[i] = ENCH_NONE;
-
- do
- {
- menv[imn].x = random2(GXM - 20) + 10;
- menv[imn].y = random2(GYM - 20) + 10;
- }
- while ((grd[menv[imn].x][menv[imn].y] != DNGN_FLOOR)
- || (mgrd[menv[imn].x][menv[imn].y] != NON_MONSTER));
-
- mgrd[menv[imn].x][menv[imn].y] = imn;
- break;
- }
-}
-
-
-void restore_game(void)
-{
- char char_f[kFileNameSize];
- char kill_f[kFileNameSize];
- char travel_f[kFileNameSize];
-#ifdef STASH_TRACKING
- char stash_f[kFileNameSize];
-#endif
-
-#ifdef CLUA_BINDINGS
- char lua_f[kFileNameSize];
-#endif
-
-#ifdef SAVE_DIR_PATH
- snprintf( char_f, sizeof(char_f),
- SAVE_DIR_PATH "%s%d", you.your_name, (int) getuid() );
-#else
- strncpy(char_f, you.your_name, kFileNameLen);
- char_f[kFileNameLen] = 0;
-#endif
-
- strcpy(kill_f, char_f);
- strcpy(travel_f, char_f);
-#ifdef CLUA_BINDINGS
- strcpy(lua_f, char_f);
- strcat(lua_f, ".lua");
-#endif
-#ifdef STASH_TRACKING
- strcpy(stash_f, char_f);
- strcat(stash_f, ".st");
-#endif
- strcat(kill_f, ".kil");
- strcat(travel_f, ".tc");
- strcat(char_f, ".sav");
-
-#ifdef DOS
- strupr(char_f);
-#ifdef STASH_TRACKING
- strupr(stash_f);
-#endif
- strupr(kill_f);
- strupr(travel_f);
-#ifdef CLUA_BINDINGS
- strupr(lua_f);
-#endif
-#endif
-
- FILE *restoreFile = fopen(char_f, "rb");
-
- if (restoreFile == NULL)
- {
- strcpy(info, "Unable to open \"");
- strcat(info, char_f );
- strcat(info, "\" for reading!");
- perror(info);
- end(-1);
- }
-
- char majorVersion = 0;
- char minorVersion = 0;
-
- if (!determine_version(restoreFile, majorVersion, minorVersion))
- {
- perror("\nSavefile appears to be invalid.\n");
- end(-1);
- }
-
- restore_version(restoreFile, majorVersion, minorVersion);
-
- // sanity check - EOF
- if (!feof(restoreFile))
- {
- snprintf( info, INFO_SIZE, "\nIncomplete read of \"%s\" - aborting.\n", char_f);
- perror(info);
- end(-1);
- }
-
- fclose(restoreFile);
-
-#ifdef STASH_TRACKING
- FILE *stashFile = fopen(stash_f, "rb");
- if (stashFile)
- {
- stashes.load(stashFile);
- fclose(stashFile);
- }
-#endif
-
-#ifdef CLUA_BINDINGS
- clua.execfile( lua_f );
-#endif // CLUA_BINDINGS
-
- FILE *travelFile = fopen(travel_f, "rb");
- if (travelFile)
- {
- travel_cache.load(travelFile);
- fclose(travelFile);
- }
-
- FILE *killFile = fopen(kill_f, "rb");
- if (killFile)
- {
- you.kills.load(killFile);
- fclose(killFile);
- }
-}
-
-static bool determine_version( FILE *restoreFile,
- char &majorVersion, char &minorVersion )
-{
- // read first two bytes.
- char buf[2];
- if (read2(restoreFile, buf, 2) != 2)
- return false; // empty file?
-
- // otherwise, read version and validate.
- majorVersion = buf[0];
- minorVersion = buf[1];
-
- if (majorVersion == SAVE_MAJOR_VERSION)
- return true;
-
- return false; // if its not 0, no idea
-}
-
-static void restore_version( FILE *restoreFile,
- char majorVersion, char minorVersion )
-{
- // assuming the following check can be removed once we can read all
- // savefile versions.
- if (majorVersion != SAVE_MAJOR_VERSION)
- {
- snprintf( info, INFO_SIZE, "\nSorry, this release cannot read a v%d.%d savefile.\n",
- majorVersion, minorVersion);
- perror(info);
- end(-1);
- }
-
- switch(majorVersion)
- {
- case SAVE_MAJOR_VERSION:
- restore_tagged_file(restoreFile, TAGTYPE_PLAYER, minorVersion);
- break;
- default:
- break;
- }
-}
-
-// generic v4 restore function
-static void restore_tagged_file( FILE *restoreFile, int fileType,
- char minorVersion )
-{
- int i;
-
- char tags[NUM_TAGS];
- tag_set_expected(tags, fileType);
-
- while(1)
- {
- i = tag_read(restoreFile, minorVersion);
- if (i == 0) // no tag!
- break;
- tags[i] = 0; // tag read
- }
-
- // go through and init missing tags
- for(i=0; i<NUM_TAGS; i++)
- {
- if (tags[i] == 1) // expected but never read
- tag_missing(i, minorVersion);
- }
-}
-
-static bool determine_level_version( FILE *levelFile,
- char &majorVersion, char &minorVersion )
-{
- // read first two bytes.
- char buf[2];
- if (read2(levelFile, buf, 2) != 2)
- return false; // empty file?
-
- // otherwise, read version and validate.
- majorVersion = buf[0];
- minorVersion = buf[1];
-
- if (majorVersion == SAVE_MAJOR_VERSION)
- return true;
-
- return false; // if its not SAVE_MAJOR_VERSION, no idea
-}
-
-static void restore_level_version( FILE *levelFile,
- char majorVersion, char minorVersion )
-{
- // assuming the following check can be removed once we can read all
- // savefile versions.
- if (majorVersion != SAVE_MAJOR_VERSION)
- {
- snprintf( info, INFO_SIZE, "\nSorry, this release cannot read a v%d.%d level file.\n",
- majorVersion, minorVersion);
- perror(info);
- end(-1);
- }
-
- switch(majorVersion)
- {
- case SAVE_MAJOR_VERSION:
- restore_tagged_file(levelFile, TAGTYPE_LEVEL, minorVersion);
- break;
- default:
- break;
- }
-}
-
-static bool determine_ghost_version( FILE *ghostFile,
- char &majorVersion, char &minorVersion )
-{
- // read first two bytes.
- char buf[2];
- if (read2(ghostFile, buf, 2) != 2)
- return false; // empty file?
-
- // check for pre-v4 -- simply started right in with ghost name.
- if (isprint(buf[0]) && buf[0] > 4)
- {
- majorVersion = 0;
- minorVersion = 0;
- rewind(ghostFile);
- return true;
- }
-
- // otherwise, read version and validate.
- majorVersion = buf[0];
- minorVersion = buf[1];
-
- if (majorVersion == SAVE_MAJOR_VERSION)
- return true;
-
- return false; // if its not SAVE_MAJOR_VERSION, no idea!
-}
-
-static void restore_ghost_version( FILE *ghostFile,
- char majorVersion, char minorVersion )
-{
- switch(majorVersion)
- {
- case SAVE_MAJOR_VERSION:
- restore_tagged_file(ghostFile, TAGTYPE_GHOST, minorVersion);
- break;
- default:
- break;
- }
-}
-
-void save_ghost( bool force )
-{
- char cha_fil[kFileNameSize];
- const int wpn = you.equip[EQ_WEAPON];
-
- if (!force && (you.your_level < 2 || you.is_undead))
- return;
-
- make_filename( cha_fil, "bones", you.your_level, you.where_are_you,
- (you.level_type != LEVEL_DUNGEON), true );
-
- FILE *gfile = fopen(cha_fil, "rb");
-
- // don't overwrite existing bones!
- if (gfile != NULL)
- {
- fclose(gfile);
- return;
- }
-
- memcpy(ghost.name, you.your_name, 20);
-
- ghost.values[ GVAL_MAX_HP ] = ((you.hp_max >= 150) ? 150 : you.hp_max);
- ghost.values[ GVAL_EV ] = player_evasion();
- ghost.values[ GVAL_AC ] = player_AC();
- ghost.values[ GVAL_SEE_INVIS ] = player_see_invis();
- ghost.values[ GVAL_RES_FIRE ] = player_res_fire();
- ghost.values[ GVAL_RES_COLD ] = player_res_cold();
- ghost.values[ GVAL_RES_ELEC ] = player_res_electricity();
-
- /* note - as ghosts, automatically get res poison + prot_life */
-
- int d = 4;
- int e = 0;
-
- if (wpn != -1)
- {
- if (you.inv[wpn].base_type == OBJ_WEAPONS
- || you.inv[wpn].base_type == OBJ_STAVES)
- {
- d = property( you.inv[wpn], PWPN_DAMAGE );
-
- d *= 25 + you.skills[weapon_skill( you.inv[wpn].base_type,
- you.inv[wpn].sub_type )];
- d /= 25;
-
- if (you.inv[wpn].base_type == OBJ_WEAPONS)
- {
- if (is_random_artefact( you.inv[wpn] ))
- e = randart_wpn_property( you.inv[wpn], RAP_BRAND );
- else
- e = you.inv[wpn].special;
- }
- }
- }
- else
- {
- /* Unarmed combat */
- if (you.species == SP_TROLL)
- d += you.experience_level;
-
- d += you.skills[SK_UNARMED_COMBAT];
- }
-
- d *= 30 + you.skills[SK_FIGHTING];
- d /= 30;
-
- d += you.strength / 4;
-
- if (d > 50)
- d = 50;
-
- ghost.values[ GVAL_DAMAGE ] = d;
- ghost.values[ GVAL_BRAND ] = e;
- ghost.values[ GVAL_SPECIES ] = you.species;
- ghost.values[ GVAL_BEST_SKILL ] = best_skill(SK_FIGHTING, (NUM_SKILLS - 1), 99);
- ghost.values[ GVAL_SKILL_LEVEL ] = you.skills[best_skill(SK_FIGHTING, (NUM_SKILLS - 1), 99)];
- ghost.values[ GVAL_EXP_LEVEL ] = you.experience_level;
- ghost.values[ GVAL_CLASS ] = you.char_class;
-
- add_spells(ghost);
-
- gfile = fopen(cha_fil, "wb");
-
- if (gfile == NULL)
- {
- strcpy(info, "Error creating ghost file: ");
- strcat(info, cha_fil);
- mpr(info);
- more();
- return;
- }
-
- // 4.0-4.3 old tagged savefile (values as unsigned char)
- // 4.4 new tagged savefile (values as signed short)
- write_tagged_file( gfile, SAVE_MAJOR_VERSION, 4, TAGTYPE_GHOST );
-
- fclose(gfile);
-
-#if DEBUG_DIAGNOSTICS
- mpr( "Saved ghost.", MSGCH_DIAGNOSTICS );
-#endif
-
-#ifdef SHARED_FILES_CHMOD_PUBLIC
- chmod(cha_fil, SHARED_FILES_CHMOD_PUBLIC);
-#endif
-} // end save_ghost()
-
-/*
- Used when creating ghosts: goes through and finds spells for the ghost to
- cast. Death is a traumatic experience, so ghosts only remember a few spells.
- */
-void add_spells( struct ghost_struct &gs )
-{
- int i = 0;
-
- for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
- gs.values[i] = SPELL_NO_SPELL;
-
- gs.values[ GVAL_SPELL_1 ] = search_first_list(SPELL_NO_SPELL);
- gs.values[ GVAL_SPELL_2 ] = search_first_list(gs.values[GVAL_SPELL_1]);
- gs.values[ GVAL_SPELL_3 ] = search_second_list(SPELL_NO_SPELL);
- gs.values[ GVAL_SPELL_4 ] = search_third_list(SPELL_NO_SPELL);
-
- if (gs.values[ GVAL_SPELL_4 ] == SPELL_NO_SPELL)
- gs.values[ GVAL_SPELL_4 ] = search_first_list(SPELL_NO_SPELL);
-
- gs.values[ GVAL_SPELL_5 ] = search_first_list(gs.values[GVAL_SPELL_4]);
-
- if (gs.values[ GVAL_SPELL_5 ] == SPELL_NO_SPELL)
- gs.values[ GVAL_SPELL_5 ] = search_first_list(gs.values[GVAL_SPELL_4]);
-
- if (player_has_spell( SPELL_DIG ))
- gs.values[ GVAL_SPELL_5 ] = SPELL_DIG;
-
- /* Looks for blink/tport for emergency slot */
- if (player_has_spell( SPELL_CONTROLLED_BLINK )
- || player_has_spell( SPELL_BLINK ))
- {
- gs.values[ GVAL_SPELL_6 ] = SPELL_CONTROLLED_BLINK;
- }
-
- if (player_has_spell( SPELL_TELEPORT_SELF ))
- gs.values[ GVAL_SPELL_6 ] = SPELL_TELEPORT_SELF;
-
- for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
- gs.values[i] = translate_spell( gs.values[i] );
-} // end add_spells()
-
-unsigned char search_first_list(unsigned char ignore_spell)
-{
- for (int i = 0; i < 20; i++)
- {
- if (search_order_conj[i] == SPELL_NO_SPELL)
- return SPELL_NO_SPELL;
-
- if (search_order_conj[i] == ignore_spell)
- continue;
-
- if (player_has_spell(search_order_conj[i]))
- return search_order_conj[i];
- }
-
- return SPELL_NO_SPELL;
-} // end search_first_list()
-
-unsigned char search_second_list(unsigned char ignore_spell)
-{
- for (int i = 0; i < 20; i++)
- {
- if (search_order_third[i] == SPELL_NO_SPELL)
- return SPELL_NO_SPELL;
-
- if (search_order_third[i] == ignore_spell)
- continue;
-
- if (player_has_spell(search_order_third[i]))
- return search_order_third[i];
- }
-
- return SPELL_NO_SPELL;
-} // end search_second_list()
-
-unsigned char search_third_list(unsigned char ignore_spell)
-{
- for (int i = 0; i < 20; i++)
- {
- if (search_order_misc[i] == SPELL_NO_SPELL)
- return SPELL_NO_SPELL;
-
- if (search_order_misc[i] == ignore_spell)
- continue;
-
- if (player_has_spell(search_order_misc[i]))
- return search_order_misc[i];
- }
-
- return SPELL_NO_SPELL;
-} // end search_third_list()
-
-
-/*
- When passed the number for a player spell, returns the equivalent monster
- spell. Returns SPELL_NO_SPELL on failure (no equiv).
- */
-unsigned char translate_spell(unsigned char spel)
-{
- switch (spel)
- {
- case SPELL_TELEPORT_SELF:
- return (MS_TELEPORT);
-
- case SPELL_MAGIC_DART:
- return (MS_MMISSILE);
- case SPELL_FIREBALL:
- case SPELL_DELAYED_FIREBALL:
- return (MS_FIREBALL);
- case SPELL_DIG:
- return (MS_DIG);
- case SPELL_BOLT_OF_FIRE:
- return (MS_FIRE_BOLT);
- case SPELL_BOLT_OF_COLD:
- return (MS_COLD_BOLT);
- case SPELL_LIGHTNING_BOLT:
- return (MS_LIGHTNING_BOLT);
- case SPELL_POLYMORPH_OTHER:
- return (MS_MUTATION);
- case SPELL_SLOW:
- return (MS_SLOW);
- case SPELL_HASTE:
- return (MS_HASTE);
- case SPELL_PARALYZE:
- return (MS_PARALYSIS);
- case SPELL_CONFUSE:
- return (MS_CONFUSE);
- case SPELL_INVISIBILITY:
- return (MS_INVIS);
- case SPELL_THROW_FLAME:
- return (MS_FLAME);
- case SPELL_THROW_FROST:
- return (MS_FROST);
- case SPELL_CONTROLLED_BLINK:
- return (MS_BLINK); /* approximate */
-/* case FREEZING_CLOUD: return ; no freezing/mephitic cloud yet
- case MEPHITIC_CLOUD: return ; */
- case SPELL_VENOM_BOLT:
- return (MS_VENOM_BOLT);
- case SPELL_POISON_ARROW:
- return (MS_POISON_ARROW);
- case SPELL_TELEPORT_OTHER:
- return (MS_TELEPORT_OTHER);
- case SPELL_SUMMON_SMALL_MAMMAL:
- return (MS_VAMPIRE_SUMMON); /* approximate */
- case SPELL_BOLT_OF_DRAINING:
- return (MS_NEGATIVE_BOLT);
- case SPELL_LEHUDIBS_CRYSTAL_SPEAR:
- return (MS_CRYSTAL_SPEAR);
- case SPELL_BLINK:
- return (MS_BLINK);
- case SPELL_ISKENDERUNS_MYSTIC_BLAST:
- return (MS_ORB_ENERGY);
- case SPELL_SUMMON_HORRIBLE_THINGS:
- return (MS_LEVEL_SUMMON); /* approximate */
- case SPELL_ANIMATE_DEAD:
- return (MS_ANIMATE_DEAD);
- case SPELL_PAIN:
- return (MS_PAIN);
- case SPELL_SUMMON_WRAITHS:
- return (MS_SUMMON_UNDEAD); /* approximate */
- case SPELL_STICKY_FLAME:
- return (MS_STICKY_FLAME);
- case SPELL_CALL_IMP:
- return (MS_SUMMON_DEMON_LESSER);
- case SPELL_BANISHMENT:
- return (MS_BANISHMENT);
- case SPELL_STING:
- return (MS_STING);
- case SPELL_SUMMON_DEMON:
- return (MS_SUMMON_DEMON);
- case SPELL_DEMONIC_HORDE:
- return (MS_SUMMON_DEMON_LESSER);
- case SPELL_SUMMON_GREATER_DEMON:
- return (MS_SUMMON_DEMON_GREATER);
- case SPELL_BOLT_OF_IRON:
- return (MS_IRON_BOLT);
- case SPELL_STONE_ARROW:
- return (MS_STONE_ARROW);
- case SPELL_DISINTEGRATE:
- return (MS_DISINTEGRATE);
- case SPELL_AGONY:
- /* Too powerful to give ghosts Torment for Agony? Nah. */
- return (MS_TORMENT);
- case SPELL_SYMBOL_OF_TORMENT:
- return (MS_TORMENT);
- default:
- break;
- }
-
- return (MS_NO_SPELL);
-}
-
-void generate_random_demon(void)
-{
- int rdem = 0;
- int i = 0;
-
- for (rdem = 0; rdem < MAX_MONSTERS + 1; rdem++)
- {
- if (rdem == MAX_MONSTERS)
- return;
-
- if (menv[rdem].type == MONS_PANDEMONIUM_DEMON)
- break;
- }
-
- char st_p[ITEMNAME_SIZE];
-
- make_name(random_int(), false, st_p);
- strcpy(ghost.name, st_p);
-
- // hp - could be defined below (as could ev, AC etc). Oh well, too late:
- ghost.values[ GVAL_MAX_HP ] = 100 + roll_dice( 3, 50 );
-
- ghost.values[ GVAL_EV ] = 5 + random2(20);
- ghost.values[ GVAL_AC ] = 5 + random2(20);
-
- ghost.values[ GVAL_SEE_INVIS ] = (one_chance_in(10) ? 0 : 1);
-
- if (!one_chance_in(3))
- ghost.values[ GVAL_RES_FIRE ] = (coinflip() ? 2 : 3);
- else
- {
- ghost.values[ GVAL_RES_FIRE ] = 0; /* res_fire */
-
- if (one_chance_in(10))
- ghost.values[ GVAL_RES_FIRE ] = -1;
- }
-
- if (!one_chance_in(3))
- ghost.values[ GVAL_RES_COLD ] = 2;
- else
- {
- ghost.values[ GVAL_RES_COLD ] = 0; /* res_cold */
-
- if (one_chance_in(10))
- ghost.values[ GVAL_RES_COLD ] = -1;
- }
-
- // demons, like ghosts, automatically get poison res. and life prot.
-
- // resist electricity:
- ghost.values[ GVAL_RES_ELEC ] = (!one_chance_in(3) ? 1 : 0);
-
- // HTH damage:
- ghost.values[ GVAL_DAMAGE ] = 20 + roll_dice( 2, 20 );
-
- // special attack type (uses weapon brand code):
- ghost.values[ GVAL_BRAND ] = SPWPN_NORMAL;
-
- if (!one_chance_in(3))
- {
- ghost.values[ GVAL_BRAND ] = random2(17);
-
- /* some brands inappropriate (eg holy wrath) */
- if (ghost.values[ GVAL_BRAND ] == SPWPN_HOLY_WRATH
- || ghost.values[ GVAL_BRAND ] == SPWPN_ORC_SLAYING
- || ghost.values[ GVAL_BRAND ] == SPWPN_PROTECTION
- || ghost.values[ GVAL_BRAND ] == SPWPN_FLAME
- || ghost.values[ GVAL_BRAND ] == SPWPN_FROST
- || ghost.values[ GVAL_BRAND ] == SPWPN_DISRUPTION)
- {
- ghost.values[ GVAL_BRAND ] = SPWPN_SPEED;
- }
- }
-
- // is demon a spellcaster?
- // upped from one_chance_in(3)... spellcasters are more interesting
- // and I expect named demons to typically have a trick or two -- bwr
- ghost.values[GVAL_DEMONLORD_SPELLCASTER] = (one_chance_in(10) ? 0 : 1);
-
- // does demon fly? (0 = no, 1 = fly, 2 = levitate)
- ghost.values[GVAL_DEMONLORD_FLY] = (one_chance_in(3) ? 0 :
- one_chance_in(5) ? 2 : 1);
-
- // vacant <ghost best skill level>:
- ghost.values[GVAL_DEMONLORD_UNUSED] = 0;
-
- // hit dice:
- ghost.values[GVAL_DEMONLORD_HIT_DICE] = 10 + roll_dice(2, 10);
-
- // does demon cycle colours?
- ghost.values[GVAL_DEMONLORD_CYCLE_COLOUR] = (one_chance_in(10) ? 1 : 0);
-
- menv[rdem].hit_dice = ghost.values[ GVAL_DEMONLORD_HIT_DICE ];
- menv[rdem].hit_points = ghost.values[ GVAL_MAX_HP ];
- menv[rdem].max_hit_points = ghost.values[ GVAL_MAX_HP ];
- menv[rdem].armour_class = ghost.values[ GVAL_AC ];
- menv[rdem].evasion = ghost.values[ GVAL_EV ];
- menv[rdem].speed = (one_chance_in(3) ? 10 : 6 + roll_dice(2, 9));
- menv[rdem].speed_increment = 70;
- menv[rdem].number = random_colour(); // demon's colour
-
- for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
- ghost.values[i] = SPELL_NO_SPELL;
-
- /* This bit uses the list of player spells to find appropriate spells
- for the demon, then converts those spells to the monster spell indices.
- Some special monster-only spells are at the end. */
- if (ghost.values[ GVAL_DEMONLORD_SPELLCASTER ] == 1)
- {
- if (coinflip())
- {
- for (;;)
- {
- if (one_chance_in(3))
- break;
-
- ghost.values[ GVAL_SPELL_1 ] = search_order_conj[i];
- i++;
-
- if (search_order_conj[i] == SPELL_NO_SPELL)
- break;
- }
- }
-
- if (coinflip())
- {
- for (;;)
- {
- if (one_chance_in(3))
- break;
-
- ghost.values[ GVAL_SPELL_2 ] = search_order_conj[i];
-
- if (search_order_conj[i] == SPELL_NO_SPELL)
- break;
- }
- }
-
- if (!one_chance_in(4))
- {
- for (;;)
- {
- if (one_chance_in(3))
- break;
-
- ghost.values[ GVAL_SPELL_3 ] = search_order_third[i];
- i++;
-
- if (search_order_third[i] == SPELL_NO_SPELL)
- break;
- }
- }
-
- if (coinflip())
- {
- for (;;)
- {
- if (one_chance_in(3))
- break;
-
- ghost.values[ GVAL_SPELL_4 ] = search_order_misc[i];
- i++;
-
- if (search_order_misc[i] == SPELL_NO_SPELL)
- break;
- }
- }
-
- if (coinflip())
- {
- for(;;)
- {
- if (one_chance_in(3))
- break;
-
- ghost.values[ GVAL_SPELL_5 ] = search_order_misc[i];
- i++;
-
- if (search_order_misc[i] == SPELL_NO_SPELL)
- break;
- }
- }
-
- if (coinflip())
- ghost.values[ GVAL_SPELL_6 ] = SPELL_BLINK;
- if (coinflip())
- ghost.values[ GVAL_SPELL_6 ] = SPELL_TELEPORT_SELF;
-
- /* Converts the player spell indices to monster spell ones */
- for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++)
- ghost.values[i] = translate_spell( ghost.values[i] );
-
- /* give demon a chance for some monster-only spells: */
- /* and demon-summoning should be fairly common: */
- if (one_chance_in(25))
- ghost.values[GVAL_SPELL_1] = MS_HELLFIRE_BURST;
- if (one_chance_in(25))
- ghost.values[GVAL_SPELL_1] = MS_METAL_SPLINTERS;
- if (one_chance_in(25))
- ghost.values[GVAL_SPELL_1] = MS_ENERGY_BOLT; /* eye of devas */
-
- if (one_chance_in(25))
- ghost.values[GVAL_SPELL_2] = MS_STEAM_BALL;
- if (one_chance_in(25))
- ghost.values[GVAL_SPELL_2] = MS_PURPLE_BLAST;
- if (one_chance_in(25))
- ghost.values[GVAL_SPELL_2] = MS_HELLFIRE;
-
- if (one_chance_in(25))
- ghost.values[GVAL_SPELL_3] = MS_SMITE;
- if (one_chance_in(25))
- ghost.values[GVAL_SPELL_3] = MS_HELLFIRE_BURST;
- if (one_chance_in(12))
- ghost.values[GVAL_SPELL_3] = MS_SUMMON_DEMON_GREATER;
- if (one_chance_in(12))
- ghost.values[GVAL_SPELL_3] = MS_SUMMON_DEMON;
-
- if (one_chance_in(20))
- ghost.values[GVAL_SPELL_4] = MS_SUMMON_DEMON_GREATER;
- if (one_chance_in(20))
- ghost.values[GVAL_SPELL_4] = MS_SUMMON_DEMON;
-
- /* at least they can summon demons */
- if (ghost.values[17] == SPELL_NO_SPELL)
- ghost.values[GVAL_SPELL_4] = MS_SUMMON_DEMON;
-
- if (one_chance_in(15))
- ghost.values[GVAL_SPELL_5] = MS_DIG;
- }
-} // end generate_random_demon()
-
-// Largest string we'll save
-#define STR_CAP 1000
-
-using std::string;
-
-void writeShort(FILE *file, short s)
-{
- char data[2];
- // High byte first - network order
- data[0] = (char)((s >> 8) & 0xFF);
- data[1] = (char)(s & 0xFF);
-
- write2(file, data, sizeof(data));
-}
-
-short readShort(FILE *file)
-{
- unsigned char data[2];
- read2(file, (char *) data, 2);
-
- // High byte first
- return (((short) data[0]) << 8) | (short) data[1];
-}
-
-void writeByte(FILE *file, unsigned char byte)
-{
- write2(file, (char *) &byte, sizeof byte);
-}
-
-unsigned char readByte(FILE *file)
-{
- unsigned char byte;
- read2(file, (char *) &byte, sizeof byte);
- return byte;
-}
-
-void writeString(FILE* file, const string &s)
-{
- int length = s.length();
- if (length > STR_CAP) length = STR_CAP;
- writeShort(file, length);
- write2(file, s.c_str(), length);
-}
-
-string readString(FILE *file)
-{
- char buf[STR_CAP + 1];
- short length = readShort(file);
- if (length)
- read2(file, buf, length);
- buf[length] = '\0';
- return string(buf);
-}
-
-void writeLong(FILE* file, long num)
-{
- // High word first, network order
- writeShort(file, (short) ((num >> 16) & 0xFFFFL));
- writeShort(file, (short) (num & 0xFFFFL));
-}
-
-long readLong(FILE *file)
-{
- // We need the unsigned short cast even for the high word because we
- // might be on a system where long is more than 4 bytes, and we don't want
- // to sign extend the high short.
- return ((long) (unsigned short) readShort(file)) << 16 |
- (long) (unsigned short) readShort(file);
-}