Just a boring ball-of-mud checkpoint
authorMartin Read <mpread@chiark.greenend.org.uk>
Sun, 9 Mar 2014 19:56:27 +0000 (19:56 +0000)
committerMartin Read <mpread@chiark.greenend.org.uk>
Sun, 9 Mar 2014 19:56:27 +0000 (19:56 +0000)
17 files changed:
Makefile
core.hh
default.permons
display-nc.cc
display.hh
log.cc
map.cc
map.hh
monsters.cc
monsters.hh
notify-local-tty.cc
notify.hh
objects.cc
objects.hh
permon.hh
player.hh
pmon_comp

index 3529790..9c22d89 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -17,10 +17,10 @@ MINVERS:=0
 PATCHVERS:=0
 COMMON_CFLAGS:=-Wall -Wwrite-strings -Wunreachable-code -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4 -DMAJVERS=$(MAJVERS) -DMINVERS=$(MINVERS) -DPATCHVERS=$(PATCHVERS)-std=gnu11 -D_FORTIFY_SOURCE=2 -I$(srcdir)
 COMMON_CXXFLAGS:=-Wall -Wwrite-strings -Wno-unused-but-set-variable -Wredundant-decls -Wunreachable-code -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4 -DMAJVERS=$(MAJVERS) -DMINVERS=$(MINVERS) -DPATCHVERS=$(PATCHVERS) -std=gnu++11 -D_FORTIFY_SOURCE=2 -I$(srcdir)
-PRODUCTION_CFLAGS:=$(COMMON_CFLAGS)
-DEVELOPMENT_CFLAGS:=$(COMMON_CFLAGS) -g -O2 -Werror
-PRODUCTION_CXXFLAGS:=$(COMMON_CXXFLAGS)
-DEVELOPMENT_CXXFLAGS:=$(COMMON_CXXFLAGS) -g -O2 -Werror
+PRODUCTION_CFLAGS:=$(COMMON_CFLAGS) -O2 
+DEVELOPMENT_CFLAGS:=$(COMMON_CFLAGS) -g -O1 -Werror
+PRODUCTION_CXXFLAGS:=$(COMMON_CXXFLAGS) -O2 
+DEVELOPMENT_CXXFLAGS:=$(COMMON_CXXFLAGS) -g -O1 -Werror
 LIBS=-lpanelw -lncursesw -lxdg-basedir -lm
 ARCHIVEDIR:=$(GAME)-$(MAJVERS).$(MINVERS).$(PATCHVERS)
 ARCHIVENAME:=$(GAME)_$(MAJVERS).$(MINVERS).$(PATCHVERS)
@@ -99,7 +99,7 @@ combat.o: $(srcdir)/combat.cc $(srcdir)/combat.hh $(srcdir)/$(GAME).hh $(srcdir)
 
 coord.o: $(srcdir)/coord.cc $(srcdir)/coord.hh
 
-display-nc.o: $(srcdir)/display-nc.cc $(srcdir)/$(GAME).hh $(srcdir)/display.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
+display-nc.o: $(srcdir)/display-nc.cc $(srcdir)/$(GAME).hh $(srcdir)/display.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh $(srcdir)/map.hh $(srcdir)/objects.hh $(srcdir)/monsters.hh $(srcdir)/permon.hh $(srcdir)/permobj.hh
 
 log.o: $(srcdir)/log.cc $(srcdir)/objects.hh $(srcdir)/monsters.hh $(srcdir)/player.hh $(srcdir)/map.hh $(srcdir)/util.h
 
diff --git a/core.hh b/core.hh
index 3f47981..9e88c8b 100644 (file)
--- a/core.hh
+++ b/core.hh
@@ -84,14 +84,15 @@ enum Noisiness
  */
 enum Gamecolour
 {
+    Gcol_nocolour = -1,
     /* l_cyan is the last "core" colour. Colours after it may alias to one
      * of the core colours depending on the nature of your display. */
-    Gcol_l_grey, Gcol_d_grey, Gcol_red, Gcol_blue,
+    Gcol_l_grey = 0, Gcol_d_grey, Gcol_red, Gcol_blue,
     Gcol_green, Gcol_purple, Gcol_brown, Gcol_cyan,
     Gcol_white, Gcol_l_red, Gcol_l_blue, Gcol_l_green,
     Gcol_l_purple, Gcol_yellow, Gcol_l_cyan,
     /* Fancy colours now! */
-    Gcol_iron, Gcol_gold, Gcol_silver,
+    Gcol_iron, Gcol_gold, Gcol_silver, Gcol_blood, Gcol_pus,
     /* UI customizable colours */
     Gcol_prio_low, Gcol_prio_normal, Gcol_prio_alert, Gcol_prio_warn, Gcol_prio_bug
 };
@@ -131,7 +132,7 @@ enum Death {
     DEATH_LASH, DEATH_RIBBONS
 };
 
-/*! \brief Fell powers that might influence the Princess's fate
+/*! \brief Fell powers from forbidden places
  */
 enum Fell_powers {
     FePo_iron,
@@ -167,6 +168,16 @@ typedef uint32_t Obj_handle;
 class Mon;
 typedef uint32_t Mon_handle;
 
+enum Decal_tag
+{
+    NO_DECAL = -1,
+    Decal_blood,
+    Decal_ichor,
+    Decal_pus
+};
+#define MAX_DECAL (Decal_pus)
+#define NUM_DECALS (MAX_DECAL + 1)
+
 #define NO_POBJ (-1)
 #define NO_PMON (-1)
 extern const Obj_handle NO_OBJ;
index 2071cee..1dd48ee 100644 (file)
 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+monster adventurer
+ascii '@'
+utf8 "@"
+desc A typical morally stunted specimen of the dungeoneering class.
+rarity 100
+power 1
+hp 1
+mtohit 0
+mdam 1
+defence 1
+exp 0
+speed 1
+STUPID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
+
 monster newt
 ascii 'n'
 utf8 "n"
@@ -37,6 +54,9 @@ speed 0
 STUPID
 SMALL
 QUADRUPED
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # rodents
 monster rat
@@ -55,6 +75,9 @@ speed 2
 STUPID
 SMALL
 QUADRUPED
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # canids
 monster wolf
@@ -72,6 +95,9 @@ defence 6
 exp 15
 speed 2
 QUADRUPED
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # Serpents
 monster snake
@@ -89,6 +115,9 @@ experience 40
 speed 2
 STUPID
 SERPENTINE
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # thugs
 monster thug
@@ -105,6 +134,9 @@ defence 4
 experience 5
 speed 1
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 monster goon
 desc Really good at pushing people's faces through the backs of their heads.
@@ -120,6 +152,9 @@ defence 8
 experience 10
 speed 1
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # hunters
 monster hunter
@@ -140,6 +175,9 @@ experience 50
 speed 1
 ARCHER
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # fighters
 monster duellist
@@ -157,6 +195,9 @@ experience 130
 speed 1
 SMART
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 monster warlord
 desc A truly exceptional warrior, strong of arm and fleet of foot.
@@ -173,6 +214,9 @@ experience 400
 speed 2
 SMART
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # goblins
 monster goblin
@@ -189,6 +233,9 @@ defence 3
 experience 3
 speed 1
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # elves
 monster bad elf
@@ -207,6 +254,9 @@ experience 15
 speed 2
 SMART
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # Trolls
 monster troll
@@ -225,6 +275,9 @@ speed 1
 STUPID
 HUMANOID
 HUGE
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # Huge humanoids
 monster giant
@@ -243,6 +296,9 @@ speed 1
 STUPID
 HUMANOID
 HUGE
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 monster giant jarl
 desc A rarity among giants, this one has used its great strength and unusually sharp wits to persuade its dimmer brethren to obey.
@@ -259,6 +315,9 @@ experience 1000
 speed 1
 HUMANOID
 HUGE
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # wizards
 monster wizard
@@ -281,6 +340,9 @@ speed 1
 SMART
 MAGICIAN
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 monster archmage
 plural archmagi
@@ -304,6 +366,9 @@ SMART
 MAGICIAN
 RESIST_ELEC
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # zombie
 monster zombie
@@ -325,6 +390,7 @@ RESIST_COLD
 RESIST_POIS
 RESIST_NECR
 HUMANOID
+MADE_OF_MEAT
 
 # Wraiths
 monster wraith
@@ -374,6 +440,7 @@ RESIST_POIS
 RESIST_NECR
 MAGICIAN
 HUMANOID
+MADE_OF_BONE
 
 monster master lich
 plural master liches
@@ -400,6 +467,7 @@ RESIST_POIS
 RESIST_NECR
 MAGICIAN
 HUMANOID
+MADE_OF_BONE
 
 # Vampires
 monster vampire
@@ -421,6 +489,8 @@ RESIST_COLD
 RESIST_POIS
 RESIST_NECR
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
 
 # 4th tier demons
 monster fire imp
@@ -440,6 +510,7 @@ DEMONIC
 RESIST_FIRE
 FLYING
 HUMANOID
+MADE_OF_MEAT
 
 # 3rd tier demons
 monster demon
@@ -459,6 +530,7 @@ SMART
 DEMONIC
 RESIST_FIRE
 HUMANOID
+MADE_OF_MEAT
 
 # 2nd tier demons
 monster defiler
@@ -479,6 +551,7 @@ DEMONIC
 MAGICIAN
 RESIST_POIS
 AMORPHOUS
+MADE_OF_GOO
 
 # 1st tier demons
 monster putrid emissary
@@ -498,6 +571,9 @@ SMART
 DEMONIC
 RESIST_POIS
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_PUS
+BURSTS_ON_DEATH
 
 monster tormentor
 desc The faceless violet-skinned figure before you wears a strangely cut garment of pale, supple leather which would be far beyond the limits of decency if there were any indecent parts to reveal. With one hand it beckons to you, while from the other it trails a long whip of the same leather as its garment.
@@ -516,6 +592,7 @@ SMART
 DEMONIC
 RESIST_POIS
 HUMANOID
+MADE_OF_MEAT
 
 monster iron lord
 desc This loyal and unwavering servant of Verant, Great Smith of the Hells, bears arms and armour from its master's forges. Fear the iron lord!
@@ -537,6 +614,8 @@ RESIST_FIRE
 RESIST_COLD
 RESIST_SLAM
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
 
 monster centaur
 desc A strange magical hybrid of horse and man.
@@ -552,6 +631,9 @@ defence 10
 experience 50
 speed 2
 CENTAUROID
+MADE_OF_MEAT
+LEAVES_CORPSE
+CONTAINS_BLOOD
 
 monster ice monster
 desc A ponderous, shambling half-humanoid figure of ice and snow.
@@ -573,6 +655,7 @@ speed 0
 RESIST_COLD
 ARCHER
 HUMANOID
+MADE_OF_ICE
 
 monster dragon
 desc A bulky beast of scales and fangs and fumes, capable of spewing searing flames to roast its enemies and rumoured to have a preference for char-grilled princess.
@@ -595,6 +678,9 @@ RESIST_FIRE
 ARCHER
 QUADRUPED
 HUGE
+MADE_OF_MEAT
+LEAVES_CORPSE
+CONTAINS_BLOOD
 
 monster moondrake
 desc A bat-winged serpent with eyes that shimmer like quicksilver and a breath that chills the living to the bone.
@@ -619,3 +705,6 @@ RESIST_NECR
 FLYING
 ARCHER
 SERPENTINE
+MADE_OF_MEAT
+LEAVES_CORPSE
+CONTAINS_BLOOD
index 5e8699b..1716361 100644 (file)
@@ -135,6 +135,8 @@ Attr_wrapper colour_data[1 + LAST_COLOUR] =
     { 0, Gcol_cyan }, /* Iron = dark cyan */
     { A_BOLD, Gcol_brown }, /* Gold = yellow */
     { A_BOLD, Gcol_l_grey }, /* Silver = white */
+    { 0, Gcol_red }, /* Blood = red */
+    { A_BOLD, Gcol_yellow }, /* Pus = yellow */
     /* UI customizable colours */
     { A_BOLD, Gcol_d_grey },
     { 0, Gcol_l_grey },
@@ -143,6 +145,14 @@ Attr_wrapper colour_data[1 + LAST_COLOUR] =
     { A_BOLD, Gcol_blue }
 };
 
+/*! \brief Game colours for decals */
+Gamecolour decal_colours[NUM_DECALS] =
+{
+    Gcol_red,
+    Gcol_l_blue,
+    Gcol_yellow
+};
+
 /*! \brief ncursesw objects for drawing terrain */
 cchar_t terrain_tiles[NUM_TERRAINS];
 
@@ -333,6 +343,34 @@ static void draw_world(void)
                 {
                     mvwchgat(world_window, i, j, 1, A_BOLD, Gcol_d_grey, nullptr);
                 }
+               else if (c != u.pos)
+               {
+                   Obj_handle obj = lvl.obj_at(c);
+                   Mon_handle mon = lvl.mon_at(c);
+                   Decal_tag decal_id = lvl.decal_at(c);
+                   if (mon != NO_MON)
+                   {
+                       // FUTURE: handle colour-customized monsters
+                   }
+                   else if (obj != NO_OBJ)
+                   {
+                       Obj const *optr = obj_snap(obj);
+                       if (optr->po_ref == PO_CORPSE)
+                       {
+                           mvwchgat(world_window, i, j, 1,
+                                    colour_data[permons[optr->meta[0]].colour].attr,
+                                    colour_data[permons[optr->meta[0]].colour].cpair,
+                                    nullptr);
+                       }
+                   }
+                   else if (decal_id != NO_DECAL)
+                   {
+                       mvwchgat(world_window, i, j, 1,
+                                colour_data[decal_colours[decal_id]].attr, 
+                                colour_data[decal_colours[decal_id]].cpair, 
+                                nullptr);
+                   }
+               }
                 front_buffer[i][j] = back_buffer[i][j];
             }
         }
@@ -667,7 +705,13 @@ void print_msg(char const *fmt, ...)
 void print_msg(Msg_prio prio, char const *fmt, ...)
 {
     va_list ap;
+    char *s;
+    int i;
+    int j;
     va_start(ap, fmt);
+    s = (char *) malloc(512);
+    i = vasprintf(&s, fmt, ap);
+    va_end(ap);
     switch (prio)
     {
     case Msg_prio::Low:
@@ -686,15 +730,17 @@ void print_msg(Msg_prio prio, char const *fmt, ...)
         wattr_set(message_window, colour_data[Gcol_prio_bug].attr, colour_data[Gcol_prio_bug].cpair, nullptr);
         break;
     }
-    vw_printw(message_window, fmt, ap);
-    wattr_set(message_window, colour_data[Gcol_prio_normal].attr, colour_data[Gcol_prio_normal].cpair, nullptr);
-    va_end(ap);
+    if (j > 50) 
+    {
+       /* TODO catch this */
+    }
+    waddstr(message_window, s);
 #ifdef DEBUG_TO_STDERR
-    va_start(ap, fmt);
-    vfprintf(stderr, fmt, ap);
-    va_end(ap);
+    fwrite(s, 1, j, stderr);
 #endif
+    wattr_set(message_window, colour_data[Gcol_prio_normal].attr, colour_data[Gcol_prio_normal].cpair, nullptr);
     display_update();
+    free(s);
 }
 
 /*! \brief Set a message and whether 'nothing' should be listed in inventory
@@ -1465,6 +1511,7 @@ void print_help(void)
     print_msg("?   a scroll\n");
     print_msg("!   a potion\n");
     print_msg("%%   some food\n");
+    print_msg("&   a corpse\n");
     print_msg("1-4 demons (smaller numbers are stronger)\n");
     print_msg("\nMost other monsters are shown as letters.\n");
     print_msg("\nThis is all the help you get. Good luck!\n");
@@ -1519,7 +1566,7 @@ static void run_main_menu(void)
             mvwprintw(fullscreen_window, 10, 1, "\n");
             mvwprintw(fullscreen_window, 11, 1, "\n");
             mvwprintw(fullscreen_window, 12, 1, "\n");
-            mvwprintw(fullscreen_window, 13, 19, "Welcome, Princess. Remind me of your name?\n");
+            mvwprintw(fullscreen_window, 13, 19, "Welcome. Remind me of your name?\n");
             mvwprintw(fullscreen_window, 14, 1, "\n");
             wmove(fullscreen_window, 14, 30);
             update_panels();
@@ -1627,7 +1674,7 @@ static void examine_square(Offset o)
     mon = lvl.mon_at(c);
     obj = lvl.obj_at(c);
     terr = lvl.terrain_at(c);
-    print_msg("%s\n", terrain_props[terr]);
+    print_msg("%s\n", terrain_props[terr].name);
     if ((mon != NO_MON) && mon_visible(mon))
     {
         print_msg("%s\n%s\n", permons[monsters[mon].pm_ref].name, permons[monsters[mon].pm_ref].description);
@@ -1687,7 +1734,7 @@ static void farlook(void)
 
 void welcome(void)
 {
-    print_msg("Welcome to the Abyss, Princess %s.\n", u.name);
+    print_msg("A welcome to you, shadowed and veiled %s.\n", u.name);
     print_msg("Press '?' for help.\n");
 }
 
@@ -1701,49 +1748,17 @@ void describe_object(Obj_handle obj)
 
 void print_obj_name(Obj_handle obj)
 {
-    Obj const *optr = obj_snap(obj);
-    Permobj const *poptr = permobjs + optr->po_ref;
-    if (optr->quan > 1)
-    {
-        print_msg("%d %s", optr->quan, poptr->plural);
-    }
-    else if (po_is_stackable(optr->po_ref))
-    {
-        print_msg("1 %s", poptr->name);
-    }
-    else if ((poptr->poclass == POCLASS_WEAPON) ||
-             (poptr->poclass == POCLASS_ARMOUR))
-    {
-        print_msg("a%s %s (%d/%d)", is_vowel(poptr->name[0]) ? "n" : "", poptr->name, optr->durability, OBJ_MAX_DUR);
-    }
-    else
-    {
-        print_msg("a%s %s", is_vowel(poptr->name[0]) ? "n" : "", poptr->name);
-    }
+    char *s;
+    asprint_obj_name(&s, obj);
+    print_msg("%s", s);
 }
 
 void print_mon_name(Mon_handle mon, int article)
 {
-    if (permons[monsters[mon].pm_ref].name[0] == '\0')
-    {
-        print_msg("GROB THE VOID (%d)", monsters[mon].pm_ref);
-        return;
-    }
-    switch (article)
-    {
-    case 0:     /* a */
-        print_msg("a%s %s", is_vowel(permons[monsters[mon].pm_ref].name[0]) ? "n" : "", permons[monsters[mon].pm_ref].name);
-        break;
-    case 1: /* the */
-        print_msg("the %s", permons[monsters[mon].pm_ref].name);
-        break;
-    case 2: /* A */
-        print_msg("A%s %s", is_vowel(permons[monsters[mon].pm_ref].name[0]) ? "n" : "", permons[monsters[mon].pm_ref].name);
-        break;
-    case 3: /* The */
-        print_msg("The %s", permons[monsters[mon].pm_ref].name);
-        break;
-    }
+    char *s;
+    asprint_mon_name(&s, mon, article);
+    print_msg("%s", s);
+    free(s);
 }
 
 /* display-nc.cc */
index b524572..728dacd 100644 (file)
@@ -57,6 +57,7 @@ extern void show_discoveries(void);
 extern void touch_one_screen(Coord c);
 extern void welcome(void);
 extern void print_obj_name(Obj_handle obj);
+extern void print_mon_name(Mon_handle mon, int article);
 
 /* "I've changed things that need to be redisplayed" flags. */
 extern bool hard_redraw;
diff --git a/log.cc b/log.cc
index 3bb7b44..ee90474 100644 (file)
--- a/log.cc
+++ b/log.cc
@@ -48,94 +48,10 @@ static void rebuild_mapmons(void);
 int datadir_fd;
 int confdir_fd;
 
-/*! \brief Read configuration files.
- *
- * \todo Implement configuration files.
- */
-void load_config(void)
-{
-    int i;
-    i = setup_dirs("com.blackswordsonics", "obumbrata", &datadir_fd, &confdir_fd);
-    if (i < 0)
-    {
-        perror("obumbrata: couldn't access configuration directories");
-        exit(1);
-    }
-    // TODO write this function
-}
-
-/*! \brief Rewrite configuration files.
- *
- * \todo Implement configuration files.
- */
-int rewrite_config(void)
-{
-    // TODO write this function
-    return 0;
-}
-
-/*! \brief Get a list of extant saved games and character names
- *
- * Fill in the map<string,string> container *dest using character names
- * as keys and filenames as values.
+/*! \brief call system(cmd) and throw an exception on failure
  *
- * \param dest Pointer to map<string,string> container to fill with data
- * \return Number of charname/filename pairs written to container
- * \todo Basic implementation
- * \todo Deal gracefully with a savefile going away before we open it
- */
-int get_savefile_list(std::map<std::string, std::string> *dest)
-{
-    // TODO write this function
-    return 0;
-}
-
-/*! \brief Record the details of the Princess's death to the log
+ * \todo throw a proper exception
  */
-void log_death(Death d, char const *what)
-{
-    FILE *fp;
-    int fd;
-    fd = openat(datadir_fd, "obumbrata.log", O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
-    if (fd == -1)
-    {
-        return;
-    }
-    fp = fdopen(fd, "a");
-    if (fp)
-    {
-        switch (d)
-        {
-        case DEATH_KILLED:
-            fprintf(fp, "%s was killed by %s.\n", u.name, what);
-            break;
-        case DEATH_KILLED_MON:
-            fprintf(fp, "%s was killed by a nasty %s.\n", u.name, what);
-            break;
-        case DEATH_BODY:
-            fprintf(fp, "%s's heart was stopped by %s.\n", u.name, what);
-            break;
-        case DEATH_AGILITY:
-            fprintf(fp, "%s's nerves were destroyed by %s.\n", u.name, what); 
-            break;
-        case DEATH_LASH:
-            fprintf(fp, "%s tasted the lash one time too many.\n", u.name);
-            break;
-        case DEATH_RIBBONS:
-            fprintf(fp, "%s looked good in ribbons.\n", u.name);
-            break;
-        }
-        fprintf(fp, "    %s died after %d ticks, with %d XP, on dungeon level %d.\n\n", u.name, game_tick, u.experience, depth);
-        fflush(fp);
-        fsync(fd);
-        fclose(fp);
-    }
-    else
-    {
-        close(fd);
-    }
-}
-
 void wrapped_system(char const *cmd)
 {
     int i = system(cmd);
@@ -145,6 +61,10 @@ void wrapped_system(char const *cmd)
     }
 }
 
+/*! \brief call fread(args) and throw an exception on failure
+ *
+ * \todo throw a proper exception
+ */
 void wrapped_fread(void *buf, size_t size, size_t nmemb, FILE *fp)
 {
     size_t i = fread(buf, size, nmemb, fp);
@@ -154,95 +74,113 @@ void wrapped_fread(void *buf, size_t size, size_t nmemb, FILE *fp)
     }
 }
 
+/*! \brief Read an unsigned 32-bit integer in network byte order */
 static inline void deserialize_uint32(FILE *fp, uint32_t *i)
 {
     wrapped_fread(i, 1, sizeof *i, fp);
     *i = ntohl(*i);
 }
 
-static inline void deserialize_int32(FILE *fp, int32_t *i)
+/*! \brief Write an unsigned 32-bit integer in network byte order */
+static inline void serialize_uint32(FILE *fp, uint32_t i)
 {
-    int32_t tmp;
-    wrapped_fread(&tmp, 1, sizeof tmp, fp);
-    *i = (int32_t) ntohl(tmp);
+    i = htonl(i);
+    fwrite(&i, 1, sizeof i, fp);
 }
 
-static inline void deserialize_int(FILE *fp, int *i)
+/*! \brief Read an signed 32-bit integer in network byte order */
+static inline void deserialize_int32(FILE *fp, int32_t *i)
 {
     int32_t tmp;
     wrapped_fread(&tmp, 1, sizeof tmp, fp);
     *i = (int32_t) ntohl(tmp);
 }
 
-static inline void deserialize_coord(FILE *fp, Coord * c)
+/*! \brief Write a signed 32-bit integer in network byte order */
+static inline void serialize_int32(FILE *fp, int32_t i)
 {
-    deserialize_int32(fp, &(c->y));
-    deserialize_int32(fp, &(c->x));
+    int32_t tmp = htonl(i);
+    fwrite(&tmp, 1, sizeof tmp, fp);
 }
 
-static inline void serialize_uint32(FILE *fp, uint32_t i)
+/*! \brief Read an int in network byte order */
+static inline void deserialize_int(FILE *fp, int *i)
 {
-    i = htonl(i);
-    fwrite(&i, 1, sizeof i, fp);
+    int32_t tmp;
+    wrapped_fread(&tmp, 1, sizeof tmp, fp);
+    *i = (int32_t) ntohl(tmp);
 }
 
-static inline void serialize_int32(FILE *fp, int32_t i)
+/*! \brief Write an int in network byte order */
+static inline void serialize_int(FILE *fp, int i)
 {
     int32_t tmp = htonl(i);
     fwrite(&tmp, 1, sizeof tmp, fp);
 }
 
-static inline void serialize_int(FILE *fp, int i)
+/*! \brief Read a Coord */
+static inline void deserialize_coord(FILE *fp, Coord * c)
 {
-    int32_t tmp = htonl(i);
-    fwrite(&tmp, 1, sizeof tmp, fp);
+    deserialize_int32(fp, &(c->y));
+    deserialize_int32(fp, &(c->x));
 }
 
+/*! \brief Write a Coord */
 static inline void serialize_coord(FILE *fp, Coord const& c)
 {
     serialize_int32(fp, c.y);
     serialize_int32(fp, c.x);
 }
 
-static inline void serialize_monhandle(FILE *fp, Mon_handle m)
+/*! \brief Read an Offset */
+static inline void deserialize_offset(FILE *fp, Offset * o)
 {
-    uint32_t tmp = htonl(m);
-    fwrite(&tmp, 1, sizeof tmp, fp);
+    deserialize_int32(fp, &(o->y));
+    deserialize_int32(fp, &(o->x));
 }
 
-static void serialize_monster(FILE *fp, Mon const& mon)
+/*! \brief Write an Offset */
+static inline void serialize_offset(FILE *fp, Coord const& o)
 {
-    serialize_monhandle(fp, mon.self);
-    serialize_int(fp, mon.pm_ref);
-    serialize_uint32(fp, mon.flags);
-    serialize_coord(fp, mon.pos);
-    serialize_coord(fp, mon.ai_lastpos);
-    serialize_int(fp, mon.hpmax);
-    serialize_int(fp, mon.hpcur);
-    serialize_int(fp, mon.mtohit);
-    serialize_int(fp, mon.rtohit);
-    serialize_int(fp, mon.defence);
-    serialize_int(fp, mon.mdam);
-    serialize_int(fp, mon.rdam);
-    serialize_int(fp, mon.next_summon);
+    serialize_int32(fp, o.y);
+    serialize_int32(fp, o.x);
 }
 
-static inline void serialize_objhandle(FILE *fp, Obj_handle o)
+/*! \brief Read a C-style string */
+static inline void deserialize_cstring(FILE *fp, char *str, uint32_t maxlen)
 {
-    uint32_t tmp = htonl(o);
-    fwrite(&tmp, 1, sizeof tmp, fp);
+    uint32_t i;
+    deserialize_uint32(fp, &i);
+    if (i >= maxlen)
+    {
+        // reading garbage
+        throw(-1);
+    }
+    else
+    {
+        memset(str, '\0', maxlen);
+        wrapped_fread(str, 1, i, fp);
+    }
 }
 
-static inline void serialize_object(FILE *fp, Obj const& obj)
+/*! \brief Write a C-style string */
+static inline void serialize_cstring(FILE *fp, char const *str, uint32_t lencheck)
 {
-    serialize_objhandle(fp, obj.self);
-    serialize_int(fp, obj.po_ref);
-    serialize_uint32(fp, obj.flags);
-    serialize_coord(fp, obj.pos);
-    serialize_int(fp, obj.quan);
-    serialize_int(fp, obj.durability);
+    size_t i = strlen(str);
+    if (i >= lencheck)
+    {
+        // welp!
+        serialize_uint32(fp, 8);
+        fwrite("(badstr)", 1, 8, fp);
+    }
+    else
+    {
+        serialize_uint32(fp, (uint32_t) i);
+        fwrite(str, 1, i, fp);
+    }
 }
 
+/*! \brief Read a monster handle */
 static inline void deserialize_monhandle(FILE *fp, Mon_handle *m)
 {
     uint32_t tmp;
@@ -250,6 +188,14 @@ static inline void deserialize_monhandle(FILE *fp, Mon_handle *m)
     *m = htonl(tmp);
 }
 
+/*! \brief Write a monster handle */
+static inline void serialize_monhandle(FILE *fp, Mon_handle m)
+{
+    uint32_t tmp = htonl(m);
+    fwrite(&tmp, 1, sizeof tmp, fp);
+}
+
+/*! \brief Read an object handle */
 static inline void deserialize_objhandle(FILE *fp, Obj_handle *o)
 {
     uint32_t tmp;
@@ -257,16 +203,14 @@ static inline void deserialize_objhandle(FILE *fp, Obj_handle *o)
     *o = htonl(tmp);
 }
 
-static inline void deserialize_object(FILE *fp, Obj *obj)
+/*! \brief Write an object handle */
+static inline void serialize_objhandle(FILE *fp, Obj_handle o)
 {
-    deserialize_objhandle(fp, &(obj->self));
-    deserialize_int(fp, &(obj->po_ref));
-    deserialize_uint32(fp, &(obj->flags));
-    deserialize_coord(fp, &(obj->pos));
-    deserialize_int(fp, &(obj->quan));
-    deserialize_int(fp, &(obj->durability));
+    uint32_t tmp = htonl(o);
+    fwrite(&tmp, 1, sizeof tmp, fp);
 }
 
+/*! \brief Read a monster */
 static void deserialize_monster(FILE *fp, Mon * mon)
 {
     deserialize_monhandle(fp, &(mon->self));
@@ -284,11 +228,133 @@ static void deserialize_monster(FILE *fp, Mon * mon)
     deserialize_int(fp, &(mon->next_summon));
 }
 
+/*! \brief Write a monster */
+static void serialize_monster(FILE *fp, Mon const& mon)
+{
+    serialize_monhandle(fp, mon.self);
+    serialize_int(fp, mon.pm_ref);
+    serialize_uint32(fp, mon.flags);
+    serialize_coord(fp, mon.pos);
+    serialize_coord(fp, mon.ai_lastpos);
+    serialize_int(fp, mon.hpmax);
+    serialize_int(fp, mon.hpcur);
+    serialize_int(fp, mon.mtohit);
+    serialize_int(fp, mon.rtohit);
+    serialize_int(fp, mon.defence);
+    serialize_int(fp, mon.mdam);
+    serialize_int(fp, mon.rdam);
+    serialize_int(fp, mon.next_summon);
+}
+
+/*! \brief Read an object */
+static void deserialize_object(FILE *fp, Obj *obj)
+{
+    deserialize_objhandle(fp, &(obj->self));
+    deserialize_int(fp, &(obj->po_ref));
+    deserialize_uint32(fp, &(obj->flags));
+    deserialize_coord(fp, &(obj->pos));
+    deserialize_int(fp, &(obj->quan));
+    for (int i = 0; i < 4; ++i)
+    {
+        deserialize_uint32(fp, &(obj->meta[i]));
+    }
+    deserialize_int(fp, &(obj->durability));
+}
+
+/*! \brief Write an object */
+static void serialize_object(FILE *fp, Obj const& obj)
+{
+    serialize_objhandle(fp, obj.self);
+    serialize_int(fp, obj.po_ref);
+    serialize_uint32(fp, obj.flags);
+    serialize_coord(fp, obj.pos);
+    serialize_int(fp, obj.quan);
+    for (int i = 0; i < 4; ++i)
+    {
+        serialize_uint32(fp, obj.meta[i]);
+    }
+    serialize_int(fp, obj.durability);
+}
+
+/*! \brief Read the player */
+static void deserialize_player(FILE *fp, Player *player)
+{
+    deserialize_cstring(fp, player->name, 17);
+    deserialize_monhandle(fp, &(player->mh));
+    deserialize_coord(fp, &(player->pos));
+    deserialize_int(fp, &(player->body));
+    deserialize_int(fp, &(player->bdam));
+    deserialize_int(fp, &(player->agility));
+    deserialize_int(fp, &(player->adam));
+    deserialize_int(fp, &(player->hpmax));
+    deserialize_int(fp, &(player->hpcur));
+    deserialize_int(fp, &(player->food));
+    deserialize_int(fp, &(player->experience));
+    deserialize_int(fp, &(player->defence));
+    deserialize_int(fp, &(player->protection));
+    deserialize_int(fp, &(player->leadfoot));
+    deserialize_int(fp, &(player->withering));
+    deserialize_int(fp, &(player->armourmelt));
+    deserialize_int(fp, &(player->speed));
+    for (int i = 0; i < DT_COUNT; ++i)
+    {
+        deserialize_uint32(fp, &(player->resistances[i]));
+    }
+    for (int i = 0; i < INVENTORY_SIZE; ++i)
+    {
+        deserialize_objhandle(fp, &(player->inventory[i]));
+    }
+    deserialize_objhandle(fp, &(player->weapon));
+    deserialize_objhandle(fp, &(player->armour));
+    deserialize_objhandle(fp, &(player->ring));
+    for (int i = 0; i < TOTAL_FELL_POWERS; ++i)
+    {
+        deserialize_int(fp, &(player->sympathy[i]));
+    }
+}
+
+/*! \brief Write the player */
+static void serialize_player(FILE *fp, Player const& player)
+{
+    serialize_cstring(fp, player.name, 17);
+    serialize_monhandle(fp, player.mh);
+    serialize_coord(fp, player.pos);
+    serialize_int(fp, player.body);
+    serialize_int(fp, player.bdam);
+    serialize_int(fp, player.agility);
+    serialize_int(fp, player.adam);
+    serialize_int(fp, player.hpmax);
+    serialize_int(fp, player.hpcur);
+    serialize_int(fp, player.food);
+    serialize_int(fp, player.experience);
+    serialize_int(fp, player.defence);
+    serialize_int(fp, player.protection);
+    serialize_int(fp, player.leadfoot);
+    serialize_int(fp, player.withering);
+    serialize_int(fp, player.armourmelt);
+    serialize_int(fp, player.speed);
+    for (int i = 0; i < DT_COUNT; ++i)
+    {
+        serialize_uint32(fp, player.resistances[i]);
+    }
+    for (int i = 0; i < INVENTORY_SIZE; ++i)
+    {
+        serialize_objhandle(fp, player.inventory[i]);
+    }
+    serialize_objhandle(fp, player.weapon);
+    serialize_objhandle(fp, player.armour);
+    serialize_objhandle(fp, player.ring);
+    for (int i = 0; i < TOTAL_FELL_POWERS; ++i)
+    {
+        serialize_int(fp, player.sympathy[i]);
+    }
+}
+
+/*! \brief Save the game; set game_finished flag if successful */
 void save_game(void)
 {
     FILE *fp;
     int fd;
-    uint32_t tmp;
     fd = openat(datadir_fd, "obumbrata.sav", O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR | S_IWUSR);
     if (fd == -1)
     {
@@ -300,12 +366,9 @@ void save_game(void)
         close(fd);
         return;
     }
-    /* Write out the player. */
-    fwrite(&u, 1, sizeof u, fp);
-    tmp = htonl(depth);
-    fwrite(&tmp, 1, sizeof tmp, fp);
-    tmp = htonl(game_tick);
-    fwrite(&tmp, 1, sizeof tmp, fp);
+    serialize_int(fp, depth);
+    serialize_int(fp, game_tick);
+    serialize_player(fp, u);
     serialize_level(fp, &lvl);
     for (auto iter = monsters.begin(); iter != monsters.end(); ++iter)
     {
@@ -327,8 +390,7 @@ void save_game(void)
     return;
 }
 
-/*! \brief Monster map reinitialization after reload
- */
+/*! \brief Reinitialize dungeon level monster handle arrays after reload */
 static void rebuild_mapmons(void)
 {
     for (auto iter = monsters.begin(); iter != monsters.end(); ++iter)
@@ -340,8 +402,7 @@ static void rebuild_mapmons(void)
     }
 }
 
-/*! \brief Object map reinitialization after reload
- */
+/*! \brief Reinitialize dungeon level object handle arrays after reload */
 static void rebuild_mapobjs(void)
 {
     for (auto iter = objects.begin(); iter != objects.end(); ++iter)
@@ -353,6 +414,7 @@ static void rebuild_mapobjs(void)
     }
 }
 
+/*! \brief Reload and unlink a saved game */
 int load_game(void)
 {
     int fd;
@@ -367,11 +429,9 @@ int load_game(void)
     {
         return -1;
     }
-    wrapped_fread(&u, 1, sizeof u, fp);
-    wrapped_fread(&depth, 1, sizeof depth, fp);
-    depth = ntohl(depth);
-    wrapped_fread(&game_tick, 1, sizeof game_tick, fp);
-    game_tick = ntohl(game_tick);
+    deserialize_int(fp, &depth);
+    deserialize_int(fp, &game_tick);
+    deserialize_player(fp, &u);
     deserialize_level(fp, &lvl);
     while (1)
     {
@@ -405,6 +465,7 @@ int load_game(void)
     }
     rebuild_mapobjs();
     rebuild_mapmons();
+    u.mptr = mon_snapv(u.mh);
     fclose(fp);
     game_finished = false;
     look_around_you();
@@ -417,6 +478,9 @@ int load_game(void)
     return 0;
 }
 
+/*! \brief Write human-readable snapshot of the player character
+ *
+ * \todo Sanitize the player character's name, or the filename, somewhere */
 void write_char_dump(void)
 {
     FILE *fp;
@@ -464,5 +528,93 @@ void write_char_dump(void)
     fclose(fp);
 }
 
+/*! \brief Read configuration files.
+ *
+ * \todo Implement configuration files.
+ */
+void load_config(void)
+{
+    int i;
+    i = setup_dirs("com.blackswordsonics", "obumbrata", &datadir_fd, &confdir_fd);
+    if (i < 0)
+    {
+        perror("obumbrata: couldn't access configuration directories");
+        exit(1);
+    }
+    // TODO write this function
+}
+
+/*! \brief Rewrite configuration files.
+ *
+ * \todo Implement configuration files.
+ */
+int rewrite_config(void)
+{
+    // TODO write this function
+    return 0;
+}
+
+/*! \brief Get a list of extant saved games and character names
+ *
+ * Fill in the map<string,string> container *dest using character names
+ * as keys and filenames as values.
+ *
+ * \param dest Pointer to map<string,string> container to fill with data
+ * \return Number of charname/filename pairs written to container
+ * \todo Basic implementation
+ * \todo Deal gracefully with a savefile going away before we open it
+ */
+int get_savefile_list(std::map<std::string, std::string> *dest)
+{
+    // TODO write this function
+    return 0;
+}
+
+/*! \brief Record the details of the PC's death to the log
+ */
+void log_death(Death d, char const *what)
+{
+    FILE *fp;
+    int fd;
+    fd = openat(datadir_fd, "obumbrata.log", O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+    if (fd == -1)
+    {
+        return;
+    }
+    fp = fdopen(fd, "a");
+    if (fp)
+    {
+        switch (d)
+        {
+        case DEATH_KILLED:
+            fprintf(fp, "%s was killed by %s.\n", u.name, what);
+            break;
+        case DEATH_KILLED_MON:
+            fprintf(fp, "%s was killed by a nasty %s.\n", u.name, what);
+            break;
+        case DEATH_BODY:
+            fprintf(fp, "%s's heart was stopped by %s.\n", u.name, what);
+            break;
+        case DEATH_AGILITY:
+            fprintf(fp, "%s's nerves were destroyed by %s.\n", u.name, what); 
+            break;
+        case DEATH_LASH:
+            fprintf(fp, "%s tasted the lash one time too many.\n", u.name);
+            break;
+        case DEATH_RIBBONS:
+            fprintf(fp, "%s looked good in ribbons.\n", u.name);
+            break;
+        }
+        fprintf(fp, "    %s died after %d ticks, with %d XP, on dungeon level %d.\n\n", u.name, game_tick, u.experience, depth);
+        fflush(fp);
+        fsync(fd);
+        fclose(fp);
+    }
+    else
+    {
+        close(fd);
+    }
+}
+
 /* log.cc */
 // vim:cindent:expandtab
diff --git a/map.cc b/map.cc
index 764fa10..7934185 100644 (file)
--- a/map.cc
+++ b/map.cc
@@ -71,6 +71,7 @@ Chunk::Chunk(Terrain t)
             terrain[k][m] = t;
             flags[k][m] = 0;
             region_number[k][m] = NO_REGION;
+            decals[k][m] = NO_DECAL;
         }
     }
 }
@@ -83,7 +84,6 @@ void initialize_chunks(Level *l, int height, int width, bool dense)
     int i;
     int j;
     drop_all_chunks(l);
-    l->origin_off = Stationary;
     l->chunks_high = height;
     l->chunks_wide = width;
     new_chunk_grid = new Chunk ** [height];
@@ -285,6 +285,8 @@ void leave_level(void)
         }
         iter = iter2;
     }
+    lvl.origin_off = Stationary;
+    drop_all_chunks(&lvl);
     depth++;
 }
 
@@ -745,6 +747,7 @@ void serialize_level(FILE *fp, Level const *l)
     fwrite(tmp_shorts, sizeof tmp_shorts[0], 2, fp);
     tmp_pair[0] = htonl(l->origin_off.y);
     tmp_pair[1] = htonl(l->origin_off.x);
+    fprintf(stderr, "Before saving: origin off is %d %d\n", l->origin_off.y, l->origin_off.x);
     fwrite(tmp_pair, sizeof tmp_pair[0], 2, fp);
     tmp = htonl(l->dead_space);
     fwrite(&tmp, sizeof tmp, 1, fp);
@@ -788,8 +791,9 @@ void deserialize_level(FILE *fp, Level *l)
     l->self.dungeon = ntohs(tmp_shorts[0]);
     l->self.depth = (int16_t) ntohs(tmp_shorts[1]);
     wrapped_fread(tmp_pair, sizeof tmp_pair[0], 2, fp);
-    l->origin_off.y = (int) ntohl(tmp_pair[0]);
-    l->origin_off.x = (int) ntohl(tmp_pair[1]);
+    l->origin_off.y = (int32_t) ntohl(tmp_pair[0]);
+    l->origin_off.x = (int32_t) ntohl(tmp_pair[1]);
+    fprintf(stderr, "After reloading: origin off is %d %d\n", l->origin_off.y, l->origin_off.x);
     wrapped_fread(&tmp, sizeof tmp, 1, fp);
     l->dead_space = Terrain(ntohl(tmp));
     wrapped_fread(&tmp, sizeof tmp, 1, fp);
diff --git a/map.hh b/map.hh
index 532bb03..9be8a98 100644 (file)
--- a/map.hh
+++ b/map.hh
@@ -110,18 +110,21 @@ public:
     Terrain terrain[CHUNK_EDGE][CHUNK_EDGE];
     uint32_t flags[CHUNK_EDGE][CHUNK_EDGE];
     uint32_t region_number[CHUNK_EDGE][CHUNK_EDGE];
+    Decal_tag decals[CHUNK_EDGE][CHUNK_EDGE];
     Terrain terrain_at(Coord c) const { return terrain[c.y][c.x]; }
     uint32_t flags_at(Coord c) const { return flags[c.y][c.x]; }
     uint32_t region_at(Coord c) const { return region_number[c.y][c.x]; }
-    uint32_t obj_at(Coord c) const { return objs[c.y][c.x]; }
-    uint32_t mon_at(Coord c) const { return mons[c.y][c.x]; }
+    Obj_handle obj_at(Coord c) const { return objs[c.y][c.x]; }
+    Mon_handle mon_at(Coord c) const { return mons[c.y][c.x]; }
+    Decal_tag decal_at(Coord c) const { return decals[c.y][c.x]; }
     void set_terrain_at(Coord c, Terrain t) { terrain[c.y][c.x] = t; }
     void overwrite_flags_at(Coord c, uint32_t f) { flags[c.y][c.x] = f; }
     void set_flags_at(Coord c, uint32_t f) { flags[c.y][c.x] |= f; }
     void clear_flags_at(Coord c, uint32_t f) { flags[c.y][c.x] &= ~f; }
     void set_region_at(Coord c, uint32_t r) { region_number[c.y][c.x] = r; }
-    void set_obj_at(Coord c, int o) { objs[c.y][c.x] = o; }
-    void set_mon_at(Coord c, int m) { mons[c.y][c.x] = m; }
+    void set_obj_at(Coord c, Obj_handle o) { objs[c.y][c.x] = o; }
+    void set_mon_at(Coord c, Mon_handle m) { mons[c.y][c.x] = m; }
+    void set_decal_at(Coord c, Decal_tag d) { decals[c.y][c.x] = d; }
     Chunk(Terrain t = WALL);
 };
 
@@ -173,82 +176,151 @@ public:
     std::deque<Stair_detail> stairs;
     Terrain terrain_at(Coord c) const
     {
-        Coord rc = c + origin_off;
-        Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
-        Chunk const *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
-        return ch ? ch->terrain_at(c2) : dead_space;
+        if (in_bounds(c))
+        {
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk const *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            return ch ? ch->terrain_at(c2) : dead_space;
+        }
+        else
+        {
+            return dead_space;
+        }
     }
     void set_terrain_at(Coord c, Terrain t)
     {
-        /* Algorithms that want to potentially stretch the level are
-         * responsible for telling the level to stretch. */
-        Coord rc = c + origin_off;
-        Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
-        Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
-        if (ch)
+        if (in_bounds(c))
         {
-            ch->set_terrain_at(c2, t);
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            if (ch)
+            {
+                ch->set_terrain_at(c2, t);
+            }
         }
     }
     uint32_t flags_at(Coord c) const
     {
-        Coord rc = c + origin_off;
-        Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
-        Chunk const *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
-        return ch ? ch->flags_at(c2) : 0;
+        if (in_bounds(c))
+        {
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk const *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            return ch ? ch->flags_at(c2) : 0u;
+        }
+        else
+        {
+            return 0u;
+        }
     }
     void set_flags_at(Coord c, uint32_t to_set) 
     {
-        Coord rc = c + origin_off;
-        Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
-        Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
-        if (ch)
+        if (in_bounds(c))
         {
-            ch->set_flags_at(c2, to_set);
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            if (ch)
+            {
+                ch->set_flags_at(c2, to_set);
+            }
         }
     }
     void clear_flags_at(Coord c, uint32_t to_clear)
     {
-        Coord rc = c + origin_off;
-        Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
-        Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
-        if (ch)
+        if (in_bounds(c))
         {
-            ch->clear_flags_at(c2, to_clear);
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            if (ch)
+            {
+                ch->clear_flags_at(c2, to_clear);
+            }
         }
     }
     Mon_handle mon_at(Coord c) const
     {
-        Coord rc = c + origin_off;
-        Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
-        Chunk const *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
-        return ch ? ch->mon_at(c2) : NO_MON;
+        if (in_bounds(c))
+        {
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk const *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            return ch ? ch->mon_at(c2) : NO_MON;
+        }
+        else
+        {
+            return NO_MON;
+        }
     }
     void set_mon_at(Coord c, Mon_handle mon)
     {
-        Coord rc = c + origin_off;
-        Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
-        Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
-        if (ch)
+        if (in_bounds(c))
         {
-            ch->set_mon_at(c2, mon);
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            if (ch)
+            {
+                ch->set_mon_at(c2, mon);
+            }
         }
     }
     Obj_handle obj_at(Coord c) const
     {
-        Coord rc = c + origin_off;
-        Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
-        Chunk const *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
-        return ch ? ch->obj_at(c2) : NO_OBJ;
+        if (in_bounds(c))
+        {
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk const *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            return ch ? ch->obj_at(c2) : NO_OBJ;
+        }
+        else
+        {
+            return NO_OBJ;
+        }
     }
     void set_obj_at(Coord c, Obj_handle obj)
     {
-        Coord rc = c + origin_off;
-        Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
-        Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
-        if (ch)
+        if (in_bounds(c))
+        {
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            if (ch)
+            {
+                ch->set_obj_at(c2, obj);
+            }
+        }
+    }
+    Decal_tag decal_at(Coord c) const
+    {
+        if (in_bounds(c))
+        {
+
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk const *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            return ch ? ch->decal_at(c2) : NO_DECAL;
+        }
+        else
+        {
+            return NO_DECAL;
+        }
+    }
+    void set_decal_at(Coord c, Decal_tag t)
+    {
+        if (in_bounds(c))
         {
-            ch->set_obj_at(c2, obj);
+            Coord rc = c + origin_off;
+            Coord c2 = { rc.y & CHUNK_MASK, rc.x & CHUNK_MASK };
+            Chunk *ch = chunks[rc.y >> CHUNK_SHIFT][rc.x >> CHUNK_SHIFT];
+            if (ch)
+            {
+                ch->set_decal_at(c2, t);
+            }
         }
     }
     Coord random_point(int margin) const;
@@ -256,7 +328,7 @@ public:
     {
         c += origin_off;
         return !((c.y < 0) || (c.x < 0) || (c.y >= (chunks_high << CHUNK_SHIFT)) ||
-            (c.x >= (chunks_wide << CHUNK_SHIFT)));
+                 (c.x >= (chunks_wide << CHUNK_SHIFT)));
     }
     int min_y() const
     {
index 25847ad..d643081 100644 (file)
@@ -185,58 +185,58 @@ void death_drop(Mon_handle mon)
     case PM_GOBLIN:
         if (!zero_die(4))
         {
-            create_obj(PO_DAGGER, 1, 0, c);
+            create_obj_near(PO_DAGGER, 1, c);
         }
         break;
     case PM_THUG:
     case PM_GOON:
         if (!zero_die(4))
         {
-            create_obj(PO_MACE, 1, 0, c);
+            create_obj_near(PO_MACE, 1, c);
         }
         else if (!zero_die(3))
         {
-            create_obj(PO_LEATHER_ARMOUR, 1, 0, c);
+            create_obj_near(PO_LEATHER_ARMOUR, 1, c);
         }
         break;
     case PM_HUNTER:
         if (!zero_die(6))
         {
-            create_obj(PO_BOW, 1, 0, c);
+            create_obj_near(PO_BOW, 1, c);
         }
         break;
     case PM_DUELLIST:
         if (!zero_die(6))
         {
-            create_obj(PO_LONG_SWORD, 1, 0, c);
+            create_obj_near(PO_LONG_SWORD, 1, c);
         }
         break;
     case PM_WIZARD:
         if (!zero_die(4))
         {
-            create_obj_class(POCLASS_SCROLL, 1, 0, c);
+            create_obj_class_near(POCLASS_SCROLL, 1, false, c);
         }
         else if (!zero_die(3))
         {
-            create_obj_class(POCLASS_POTION, 1, 0, c);
+            create_obj_class_near(POCLASS_POTION, 1, false, c);
         }
         break;
     case PM_WARLORD:
         if (!zero_die(3))
         {
-            create_obj(PO_RUNESWORD, 1, 0, c);
+            create_obj_near(PO_RUNESWORD, 1, c);
         }
         break;
     case PM_DEMON:
         if (!zero_die(100))
         {
-            create_obj(PO_DEVIL_SPLEEN, 1, 0, c);
+            create_obj_near(PO_DEVIL_SPLEEN, 1, c);
         }
         break;
     case PM_DEFILER:
         if (!zero_die(50))
         {
-            create_obj(PO_DEVIL_SPLEEN, 1, 0, c);
+            create_obj_near(PO_DEVIL_SPLEEN, 1, c);
         }
         break;
     default:
@@ -313,14 +313,38 @@ void unplace_mon(Mon_handle mon)
     monsters[mon].flags &= ~MF_USED;
 }
 
+void apply_liquid(int pm, Coord c)
+{
+    if (pmon_bleeds(pm))
+    {
+       lvl.set_decal_at(c, Decal_blood);
+    }
+    else if (pmon_suppurates(pm))
+    {
+       lvl.set_decal_at(c, Decal_pus);
+    }
+}
+
 /*! \brief Handle the death of a monster
  *
  * \todo Support special effects on monster death
  */
-void kill_mon(Mon_handle mon, bool by_you)
+void kill_mon(Mon_handle mon, bool by_you, bool explode_corpse)
 {
+    Mon *mptr = mon_snapv(mon);
+    int pm = mptr->pm_ref;
+    apply_liquid(pm, mptr->pos);
+    if (pmon_explodes(pm) || (pmon_leaves_corpse(pm) && explode_corpse))
+    {
+       // TODO shower the surroundings with the monster's liquid
+    }
+    else if (pmon_leaves_corpse(pm))
+    {
+       create_corpse(pm, mptr->pos);
+    }
     death_drop(mon); // phat lewt!
-    monsters[mon].hpcur = -1; // Set HP to -1 so nothing will think it's alive
+    mptr->flags &= ~MF_ALIVE;
+    monsters[mon].hpcur = -1;
     unplace_mon(mon); // cleanup
     if (by_you)
     {
@@ -610,5 +634,33 @@ bool Mon::can_fly(void) const
     return pmon_can_fly(pm_ref);
 }
 
-/* monsters.c */
+void asprint_mon_name(char **buf, Mon_handle mon, int article)
+{
+    Mon const *mptr = mon_snap(mon);
+    int i;
+    switch (article)
+    {
+    case 0:
+       i = asprintf(buf, "a%s %s", is_vowel(permons[mptr->pm_ref].name[0]) ? "n" : "", permons[mptr->pm_ref].name);
+       break;
+    case 1:
+       i = asprintf(buf, "the %s", permons[mptr->pm_ref].name);
+       break;
+    case 2:
+       i = asprintf(buf, "A%s %s", is_vowel(permons[mptr->pm_ref].name[0]) ? "n" : "", permons[mptr->pm_ref].name);
+       break;
+    case 3:
+       i = asprintf(buf, "The %s", permons[mptr->pm_ref].name);
+       break;
+    default:
+       i = -1;
+    }
+    if (i == -1)
+    {
+       // TODO wibble
+    }
+    return;
+}
+
+/* monsters.cc */
 // vim:cindent
index a0eb5f9..6554eac 100644 (file)
@@ -86,7 +86,7 @@ inline Mon const *mon_snap(Mon_handle mon)
 void update_mon(Mon_handle mon);
 void mon_acts(Mon_handle mon);
 void death_drop(Mon_handle mon);
-void print_mon_name(Mon_handle mon, int article);
+void asprint_mon_name(char **buf, Mon_handle mon, int article);
 void summon_demon_near(Coord  c);
 Mon_handle create_mon(int pm_idx, Coord c);
 int summoning(Coord c, int how_many);
@@ -100,7 +100,7 @@ void reloc_mon(Mon_handle mon, Coord c);
 Pass_fail teleport_mon(Mon_handle mon);       /* Randomly relocate monster. */
 Pass_fail teleport_mon_to_you(Mon_handle mon);        /* Relocate monster to your vicinity. */
 void heal_mon(Mon_handle mon, int amount, int cansee);
-void kill_mon(Mon_handle mon, bool by_you);
+void kill_mon(Mon_handle mon, bool by_you, bool explode_corpse = false);
 void unplace_mon(Mon_handle mon);
 
 /* XXX mon2.c data and funcs */
index c976ca3..ecb5742 100644 (file)
@@ -242,6 +242,12 @@ void notify_blocked_water(void)
     print_msg("The idiot who raised you never taught you to swim.\n");
 }
 
+void notify_new_obj_at(Coord c, Obj_handle obj)
+{
+    newsym(c);
+    display_update();
+}
+
 void notify_obj_at(Coord c)
 {
     print_msg("You see here ");
index a90d738..325e670 100644 (file)
--- a/notify.hh
+++ b/notify.hh
@@ -62,6 +62,7 @@ void notify_blocked_water(void);
 void notify_cant_go(void);
 
 // Unsorted notifications
+void notify_new_obj_at(Coord c, Obj_handle obj);
 void notify_obj_at(Coord c);
 void notify_dress_shredded(void);
 void notify_mon_healed(Mon_handle mon);
index a3c198b..07914e1 100644 (file)
@@ -320,7 +320,7 @@ Obj_handle get_first_free_obj(void)
     return first_free_obj_handle++;
 }
 
-Obj_handle create_obj_class(enum poclass_num po_class, int quantity, bool with_you, Coord c)
+Obj_handle create_obj_class_near(enum poclass_num po_class, int quantity, bool with_you, Coord c)
 {
     int po_idx;
     int tryct;
@@ -347,7 +347,7 @@ Obj_handle create_obj_class(enum poclass_num po_class, int quantity, bool with_y
         }
         break;
     }
-    return create_obj(po_idx, quantity, with_you, c);
+    return (with_you ? create_obj(po_idx, quantity, with_you, c) : create_obj_near(po_idx, quantity, c));
 }
 
 int get_random_pobj(void)
@@ -372,6 +372,45 @@ int get_random_pobj(void)
     return po_idx;
 }
 
+Obj_handle create_corpse(int pm_idx, Coord c)
+{
+    Obj_handle obj = create_obj_near(PO_CORPSE, 1, c);
+    if (obj != NO_OBJ)
+    {
+       Obj *o = obj_snapv(obj);
+       o->meta[0] = pm_idx;
+       o->meta[1] = 0; // reserved
+       o->meta[2] = 0; // reserved
+       o->meta[3] = 0; // reserved
+    }
+    return obj;
+}
+
+Obj_handle create_obj_near(int po_idx, int quantity, Coord c)
+{
+    Offset delta;
+    Coord nc;
+    int panic_count = 200;
+    while ((lvl.obj_at(c) != NO_OBJ) && (panic_count > 0))
+    {
+       do
+       {
+           delta = random_step();
+           nc = c + delta;
+       }
+       while (terrain_blocks_beings(lvl.terrain_at(nc)));
+       c = nc;
+       --panic_count;
+    }
+    if (panic_count < 1)
+    {
+       // TODO debugging report for failure
+       return NO_OBJ;
+    }
+    Obj_handle obj = create_obj(po_idx, quantity, false, c);
+    return obj;
+}
+
 Obj_handle create_obj(int po_idx, int quantity, bool with_you, Coord c)
 {
     Obj_handle obj = get_first_free_obj();
@@ -399,9 +438,6 @@ Obj_handle create_obj(int po_idx, int quantity, bool with_you, Coord c)
     {
     case POCLASS_WEAPON:
     case POCLASS_ARMOUR:
-        /* Set durability of weapons and armour to a suitable value.
-         * 100 has been chosen for the moment, but this may need
-         * tuning. */
         o.durability = OBJ_MAX_DUR;
         break;
     default:
@@ -411,36 +447,48 @@ Obj_handle create_obj(int po_idx, int quantity, bool with_you, Coord c)
     if (!(o.flags & OF_WITH_YOU))
     {
         lvl.set_obj_at(c, obj);
+       notify_new_obj_at(c, obj);
     }
     return obj;
 }
 
-void sprint_obj_name(char *buf, Obj_handle obj, int len)
+void asprint_obj_name(char **buf, Obj_handle obj)
 {
     Obj *optr;
     Permobj *poptr;
+    int i;
     optr = obj_snapv(obj);
     poptr = permobjs + optr->po_ref;
     if (optr->quan > 1)
     {
-        snprintf(buf, len, "%d %s", optr->quan, poptr->plural);
+        i = asprintf(buf, "%d %s", optr->quan, poptr->plural);
     }
     else if (po_is_stackable(optr->po_ref))
     {
-        snprintf(buf, len, "1 %s", poptr->name);
+        i = asprintf(buf, "1 %s", poptr->name);
     }
     else if ((poptr->poclass == POCLASS_WEAPON) ||
              (poptr->poclass == POCLASS_ARMOUR))
     {
-        snprintf(buf, len, "a%s %s (%d/%d)", is_vowel(poptr->name[0]) ? "n" : "", poptr->name, optr->durability, OBJ_MAX_DUR);
+        i = asprintf(buf, "a%s %s (%d/%d)", is_vowel(poptr->name[0]) ? "n" : "", poptr->name, optr->durability, OBJ_MAX_DUR);
+    }
+    else if (optr->po_ref == PO_CORPSE)
+    {
+       Permon *pmptr = permons + optr->meta[0];
+       i = asprintf(buf, "a%s %s corpse", is_vowel(pmptr->name[0]) ? "n" : "", pmptr->name);
     }
     else
     {
-        snprintf(buf, len, "a%s %s", is_vowel(poptr->name[0]) ? "n" : "", poptr->name);
+        i = asprintf(buf, "a%s %s", is_vowel(poptr->name[0]) ? "n" : "", poptr->name);
+    }
+    if (i == -1)
+    {
+       // TODO wibble
     }
+    return;
 }
 
-void fprint_obj_name(FILE *fp, Obj_handle obj)
+void sprint_obj_name(char *buf, Obj_handle obj, int len)
 {
     Obj *optr;
     Permobj *poptr;
@@ -448,23 +496,36 @@ void fprint_obj_name(FILE *fp, Obj_handle obj)
     poptr = permobjs + optr->po_ref;
     if (optr->quan > 1)
     {
-        fprintf(fp, "%d %s", optr->quan, poptr->plural);
+        snprintf(buf, len, "%d %s", optr->quan, poptr->plural);
     }
     else if (po_is_stackable(optr->po_ref))
     {
-        fprintf(fp, "1 %s", poptr->name);
+        snprintf(buf, len, "1 %s", poptr->name);
     }
     else if ((poptr->poclass == POCLASS_WEAPON) ||
              (poptr->poclass == POCLASS_ARMOUR))
     {
-        fprintf(fp, "a%s %s (%d/%d)", is_vowel(poptr->name[0]) ? "n" : "", poptr->name, optr->durability, OBJ_MAX_DUR);
+        snprintf(buf, len, "a%s %s (%d/%d)", is_vowel(poptr->name[0]) ? "n" : "", poptr->name, optr->durability, OBJ_MAX_DUR);
+    }
+    else if (optr->po_ref == PO_CORPSE)
+    {
+       Permon *pmptr = permons + optr->meta[0];
+       snprintf(buf, len, "a%s %s corpse", is_vowel(pmptr->name[0]) ? "n" : "", pmptr->name);
     }
     else
     {
-        fprintf(fp, "a%s %s", is_vowel(poptr->name[0]) ? "n" : "", poptr->name);
+        snprintf(buf, len, "a%s %s", is_vowel(poptr->name[0]) ? "n" : "", poptr->name);
     }
 }
 
+void fprint_obj_name(FILE *fp, Obj_handle obj)
+{
+    char *s;
+    asprint_obj_name(&s, obj);
+    fputs(s, fp);
+    free(s);
+}
+
 Action_cost drop_obj(int inv_idx)
 {
     Obj *optr;
index 90db33e..7e0396d 100644 (file)
@@ -50,17 +50,21 @@ public:
     uint32_t flags;
     int quan;
     Coord pos;
+    uint32_t meta[4]; // Things like what pmon a corpse is
     int durability;     /* Weapons and armour degrade with use. */
 };
 extern std::map<Obj_handle, Obj> objects;
 
 /* XXX objects.cc data and funcs */
+void asprint_obj_name(char **s, Obj_handle obj);
 void sprint_obj_name(char *s, Obj_handle obj, int len);
 void fprint_obj_name(FILE *fp, Obj_handle obj);
 void describe_object(Obj_handle obj);
+Obj_handle create_corpse(int pm_idx, Coord c);
+Obj_handle create_obj_near(int po_idx, int quantity, Coord c);
 Obj_handle create_obj(int po_idx, int quantity, bool with_you, Coord c);
 bool consume_obj(Obj_handle obj);
-Obj_handle create_obj_class(enum poclass_num pocl, int quantity, bool with_you, Coord c);
+Obj_handle create_obj_class_near(enum poclass_num pocl, int quantity, bool with_you, Coord c);
 void damage_obj(Obj_handle obj);
 int evasion_penalty(Obj_handle obj);
 
index 0fc6fcb..0e31244 100644 (file)
--- a/permon.hh
+++ b/permon.hh
 #define PMF_QUADRUPED   0x10000000
 #define PMF_SKITTERISH  0x20000000
 #define PMF_HUGE        0x40000000
-#define PMF_SMALL       0x80000000
+#define PMF_SMALL       0x80000000u
+
+#define PMF_MADE_OF_MEAT    0x00000001u
+#define PMF_MADE_OF_GOO     0x00000002u
+#define PMF_MADE_OF_BONE    0x00000004u
+#define PMF_MADE_OF_METAL   0x00000008u
+#define PMF_MADE_OF_ICE     0x00000010u
+#define PMF_MADE_OF_FIRE    0x00000020u
+#define PMF_CONTAINS_BLOOD  0x00010000u
+#define PMF_CONTAINS_PUS    0x00020000u
+#define PMF_BURSTS_ON_DEATH 0x40000000u
+#define PMF_LEAVES_CORPSE   0x80000000u
 
 #define PERMON_FLAG_FIELDS 2
 /*! \brief Describes the baseline stats of a specific kind of monster */
@@ -93,33 +104,37 @@ enum AI_mode {
     AI_charger, AI_archer, AI_dodger, AI_drunk, AI_seeker, AI_chaser
 };
 
-/* XXX pmon2.c data and funcs */
-extern bool pmon_is_archer(int pm);
-extern bool pmon_is_magician(int pm);
-extern bool pmon_is_smart(int pm);
-extern bool pmon_is_stupid(int pm);
-
-extern bool pmon_resists_cold(int pm);
-extern bool pmon_resists_fire(int pm);
-extern bool pmon_resists_poison(int pm);
-extern bool pmon_resists_necro(int pm);
-extern bool pmon_resists_elec(int pm);
-extern bool pmon_resists_drowning(int pm);
-extern bool pmon_resists_knockback(int pm);
-
-extern bool pmon_can_fly(int pm);
-
-extern bool pmon_is_ethereal(int pm);
-extern bool pmon_is_undead(int pm);
-extern bool pmon_is_demonic(int pm);
-
-extern bool pmon_has_hands(int pm);
-extern bool pmon_is_humanoid(int pm);
-extern bool pmon_is_quadruped(int pm);
-extern bool pmon_is_skitterish(int pm);
-extern bool pmon_is_amorphous(int pm);
-extern bool pmon_is_huge(int pm);
-extern bool pmon_is_small(int pm);
+bool pmon_is_archer(int pm);
+bool pmon_is_magician(int pm);
+bool pmon_is_smart(int pm);
+bool pmon_is_stupid(int pm);
+
+bool pmon_resists_cold(int pm);
+bool pmon_resists_fire(int pm);
+bool pmon_resists_poison(int pm);
+bool pmon_resists_necro(int pm);
+bool pmon_resists_elec(int pm);
+bool pmon_resists_drowning(int pm);
+bool pmon_resists_knockback(int pm);
+
+bool pmon_can_fly(int pm);
+
+bool pmon_is_ethereal(int pm);
+bool pmon_is_undead(int pm);
+bool pmon_is_demonic(int pm);
+
+bool pmon_has_hands(int pm);
+bool pmon_is_humanoid(int pm);
+bool pmon_is_quadruped(int pm);
+bool pmon_is_skitterish(int pm);
+bool pmon_is_amorphous(int pm);
+bool pmon_is_huge(int pm);
+bool pmon_is_small(int pm);
+
+inline bool pmon_bleeds(int pm) { return permons[pm].flags[1] & PMF_CONTAINS_BLOOD; }
+inline bool pmon_suppurates(int pm) { return permons[pm].flags[1] & PMF_CONTAINS_PUS; }
+inline bool pmon_explodes(int pm) { return permons[pm].flags[1] & PMF_BURSTS_ON_DEATH; }
+inline bool pmon_leaves_corpse(int pm) { return permons[pm].flags[1] & PMF_LEAVES_CORPSE; }
 
 #endif
 
index b7fb924..deb0864 100644 (file)
--- a/player.hh
+++ b/player.hh
 
 /*! \brief Internal representation of the player character 
  */
+#define INVENTORY_SIZE 19
 class Player {
 public:
     char name[17];  //!< Allows 16 actual characters, plus 0 terminator
+    Mon_handle mh;    //!< Handle for monster representing the player.
+    Mon *mptr;   //!< Pointer for monster representing the player.
     Coord pos;      //!< Position within current dungeon level.
     int body;       //!< Combined stamina and strength stat.
     int bdam;       //!< Current level of temporary Body drain
@@ -53,7 +56,7 @@ public:
     int speed;      //!< Controls how often you act.
     uint32_t resistances[DT_COUNT]; //!< Resistance masks per damage type
     int level;      //!< Current experience level.
-    Obj_handle inventory[19];  //!< currently carried items.
+    Obj_handle inventory[INVENTORY_SIZE];  //!< currently carried items.
     Obj_handle weapon;     //!< currently equipped weapon.
     Obj_handle armour;     //!< currently equipped armour.
     Obj_handle ring;       //!< currently equipped ring.
index e2405e1..207f50c 100755 (executable)
--- a/pmon_comp
+++ b/pmon_comp
@@ -38,6 +38,16 @@ our %flag_indices =
     'SKITTERISH' => 0,
     'HUGE' => 0,
     'SMALL' => 0,
+    'MADE_OF_MEAT' => 1,
+    'MADE_OF_GOO' => 1,
+    'MADE_OF_BONE' => 1,
+    'MADE_OF_METAL' => 1,
+    'MADE_OF_ICE' => 1,
+    'MADE_OF_FIRE' => 1,
+    'CONTAINS_BLOOD' => 1,
+    'CONTAINS_PUS' => 1,
+    'BURSTS_ON_DEATH' => 1,
+    'LEAVES_CORPSE' => 1,
 );