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)
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
*/
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
};
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,
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;
# 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"
STUPID
SMALL
QUADRUPED
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# rodents
monster rat
STUPID
SMALL
QUADRUPED
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# canids
monster wolf
exp 15
speed 2
QUADRUPED
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# Serpents
monster snake
speed 2
STUPID
SERPENTINE
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# thugs
monster thug
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.
experience 10
speed 1
HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# hunters
monster hunter
speed 1
ARCHER
HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# fighters
monster duellist
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.
speed 2
SMART
HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# goblins
monster goblin
experience 3
speed 1
HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# elves
monster bad elf
speed 2
SMART
HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# Trolls
monster troll
STUPID
HUMANOID
HUGE
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# Huge humanoids
monster giant
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.
speed 1
HUMANOID
HUGE
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# wizards
monster wizard
SMART
MAGICIAN
HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
monster archmage
plural archmagi
MAGICIAN
RESIST_ELEC
HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
# zombie
monster zombie
RESIST_POIS
RESIST_NECR
HUMANOID
+MADE_OF_MEAT
# Wraiths
monster wraith
RESIST_NECR
MAGICIAN
HUMANOID
+MADE_OF_BONE
monster master lich
plural master liches
RESIST_NECR
MAGICIAN
HUMANOID
+MADE_OF_BONE
# Vampires
monster vampire
RESIST_POIS
RESIST_NECR
HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
# 4th tier demons
monster fire imp
RESIST_FIRE
FLYING
HUMANOID
+MADE_OF_MEAT
# 3rd tier demons
monster demon
DEMONIC
RESIST_FIRE
HUMANOID
+MADE_OF_MEAT
# 2nd tier demons
monster defiler
MAGICIAN
RESIST_POIS
AMORPHOUS
+MADE_OF_GOO
# 1st tier demons
monster putrid emissary
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.
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!
RESIST_COLD
RESIST_SLAM
HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
monster centaur
desc A strange magical hybrid of horse and man.
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.
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.
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.
FLYING
ARCHER
SERPENTINE
+MADE_OF_MEAT
+LEAVES_CORPSE
+CONTAINS_BLOOD
{ 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 },
{ 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];
{
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];
}
}
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:
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
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");
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();
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);
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");
}
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 */
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;
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);
}
}
+/*! \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);
}
}
+/*! \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;
*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;
*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));
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)
{
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)
{
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)
}
}
-/*! \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)
}
}
+/*! \brief Reload and unlink a saved game */
int load_game(void)
{
int fd;
{
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)
{
}
rebuild_mapobjs();
rebuild_mapmons();
+ u.mptr = mon_snapv(u.mh);
fclose(fp);
game_finished = false;
look_around_you();
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;
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
terrain[k][m] = t;
flags[k][m] = 0;
region_number[k][m] = NO_REGION;
+ decals[k][m] = NO_DECAL;
}
}
}
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];
}
iter = iter2;
}
+ lvl.origin_off = Stationary;
+ drop_all_chunks(&lvl);
depth++;
}
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);
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);
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);
};
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;
{
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
{
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:
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)
{
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
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);
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 */
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 ");
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);
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;
}
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)
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();
{
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:
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;
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;
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);
#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 */
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
/*! \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
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.
'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,
);