GENERATED_OBJS:=permobj.o permons.o
GENERATED_SOURCE:=permobj.cc pobj_id.hh permons.cc pmon_id.hh
GENERATED_MAKES:=dirs.mk features.mk
-HANDWRITTEN_OBJS:=cave.o combat.o combo.o coord.o deeds.o display-nc.o dungeon.o fov.o log.o main.o map.o mon1.o mon2.o mon3.o notify-local-tty.o obj1.o obj2.o pmon2.o rng.o role.o shrine.o skills.o sorcery.o u.o util.o
+HANDWRITTEN_OBJS:=cave.o combat.o combo.o coord.o deeds.o display-nc.o dungeon.o fov.o levtheme.o log.o main.o map.o mon1.o mon2.o mon3.o notify-local-tty.o obj1.o obj2.o pmon2.o pobj2.o rng.o role.o shrine.o skills.o sorcery.o text.o u.o util.o
OBJS:=$(GENERATED_OBJS) $(HANDWRITTEN_OBJS)
GAME:=victrix-abyssi
MAJVERS:=0
MINVERS:=9
PATCHVERS:=2
-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)
+INCPATH:=-I$(srcdir)
+DEFINES:=-DMAJVERS=$(MAJVERS) -DMINVERS=$(MINVERS) -DPATCHVERS=$(PATCHVERS) -D_FORTIFY_SOURCE=2
+COMMON_CFLAGS:=-Wall -Wwrite-strings -Wunreachable-code -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4 -std=gnu11 $(DEFINES) $(INCPATH)
+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 -std=gnu++11 $(DEFINES) $(INCPATH)
PRODUCTION_CFLAGS:=$(COMMON_CFLAGS) -O2
DEVELOPMENT_CFLAGS:=$(COMMON_CFLAGS) -g -Werror
PRODUCTION_CXXFLAGS:=$(COMMON_CXXFLAGS) -O2
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
+levtheme.o: $(srcdir)/levtheme.cc $(srcdir)/core.hh $(srcdir)/map.hh
+
log.o: $(srcdir)/log.cc $(srcdir)/objects.hh $(srcdir)/monsters.hh $(srcdir)/player.hh $(srcdir)/map.hh $(srcdir)/util.h
main.o: $(srcdir)/main.cc $(srcdir)/combat.hh $(srcdir)/$(GAME).hh $(srcdir)/monsters.hh $(srcdir)/notify.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
obj2.o: $(srcdir)/obj2.cc $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/objects.hh $(srcdir)/monsters.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
+pmon2.o: $(srcdir)/pmon2.cc $(srcdir)/core.hh $(srcdir)/notify.hh $(srcdir)/permon.hh ./pmon_id.hh ./pobj_id.hh
+
+pobj2.o: $(srcdir)/pobj2.cc $(srcdir)/core.hh $(srcdir)/notify.hh ./pobj_id.hh ./pmon_id.hh
+
rng.o: $(srcdir)/rng.cc $(srcdir)/rng.hh
role.o: $(srcdir)/role.cc $(srcdir)/combat.hh $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/monsters.hh $(srcdir)/objects.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
sorcery.o: $(srcdir)/sorcery.cc $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/sorcery.hh $(srcdir)/objects.hh $(srcdir)/monsters.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
+text.o: $(srcdir)/text.cc $(srcdir)/text.hh
+
u.o: $(srcdir)/u.cc $(srcdir)/combat.hh $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/monsters.hh $(srcdir)/objects.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
util.o: $(srcdir)/util.c $(srcdir)/util.h
Victrix Abyssi currently only supports the Linux kernel, GNU Make, and the GNU toolchain for C/C++. However, patches to support other copyleft or permissively licensed kernels and/or toolchains will usually be considered in a favorable light.
-Patches to build Victrix Abyssi for proprietary kernels and/or with proprietary toolchains will not be carried in-tree.
+Patches to build Victrix Abyssi for proprietary kernels and/or with proprietary toolchains will not be carried in-tree, unless it is painless to do so.
Bugs and/or build failures which *only* occur when running on proprietary kernels and/or building with proprietary toolchains will be treated as being of 'wishlist' severity.
## Copyright
-Victrix Abyssi's source code is copyright © 2005-2014 Martin Read, and is released under a BSD-style license as reproduced in the file COPYING in the top-level directory of the source tree.
+Victrix Abyssi's source code is copyright © 2005-2014 Martin Read, and is released under a BSD-style license as reproduced in the file COPYING in the top-level directory of the source tree. Any file in this distribution which does not contain an inline copy of the licence text should be assumed to be under that licence.
}
/*! \brief Excavate a "pits" level. */
-void build_level_pits(Level *l, Terrain pit_type)
+void build_level_pits(Level *l)
{
+ Terrain pit_type;
+ if ((l->threat <= 5) || !zero_die(3))
+ {
+ pit_type = CHASM;
+ }
+ else if (!zero_die(2))
+ {
+ pit_type = WATER;
+ }
+ else
+ {
+ pit_type = LAVA;
+ }
Coord c = { GUIDE_EDGE_SIZE / 2, GUIDE_EDGE_SIZE / 2 };
int walk_data[4] = { 1, FLOOR, WALL, pit_type };
Greater = 1
};
-enum Pass_fail
+enum class Success
{
- You_fail = -1,
- You_pass = 0
+ fail,
+ pass
};
enum Action_cost
class Mon;
typedef uint32_t Mon_handle;
+/*! \brief Dungeon features */
+enum Terrain {
+ WALL = 0, MASONRY_WALL, AMETHYST_WALL, IRON_WALL, SKIN_WALL, BONE_WALL, DOOR, FLOOR, AMETHYST_FLOOR, IRON_FLOOR, SKIN_FLOOR, BONE_FLOOR, ALTAR, STAIRS_UP, STAIRS_DOWN, PORTAL_LANDING, PORTAL_ONWARD, CHASM, LAVA, WATER
+};
+
+#define MAX_TERRAIN (WATER)
+#define NUM_TERRAINS (1 + MAX_TERRAIN)
+
+/*! \brief Property structure for terrain types
+ *
+ * This structure contains a mixture of display-like and engine-like data
+ * (but nothing that depends on internal implementation details of the
+ * display subsystem).
+ */
+struct Terrain_props
+{
+ char const *name; //!< UTF-8 encoded English name
+ char ascii; //!< ASCII symbol
+ char const * unicode; //!< UTF-8 encoded Unicode symbol
+ Gamecolour colour; //!< colour to use in terminal-like environments
+ uint32_t flags; //!< Bitmask composed using TFLAG_* macros.
+};
+
+/*! \brief Array of terrain properties */
+extern Terrain_props terrain_props[NUM_TERRAINS];
+
+/*! \brief "Decorative" decals */
enum Decal_tag
{
NO_DECAL = -1,
extern const Mon_handle NO_MON;
#define NO_REGION 0xffffffffu
+/*! \brief ID numbers for skills */
+enum Skill_id
+{
+ NO_SKILL = -1,
+ Skill_power_attack = 0,
+ /* Princess skills */
+ Skill_blood_royal,
+ Skill_flying_leap,
+ Skill_regal_radiance,
+ /* Demon Hunter skills */
+ Skill_charge,
+ Skill_blade_dance,
+ Skill_demon_slayer,
+ /* Thanatophile skills */
+ Skill_deaths_grace,
+ Skill_deaths_vengeance,
+ Skill_sanctified_by_death
+};
+
+#define LAST_SKILL (Skill_sanctified_by_death)
+#define NUM_SKILLS (1 + LAST_SKILL)
+
struct Inferno_detail
{
bool by_you;
{
/* this actually belongs in a sensible place. */
int saved_armour = u.armour;
- if (unequip_safety_check(BONUS_ARMOUR, Noise_std) == You_pass)
+ if (unequip_safety_check(BONUS_ARMOUR, Noise_std) == Success::pass)
{
u.armour = NO_OBJ;
recalc_defence();
WEAPON dagger
PLURAL daggers
DESC A long knife, designed for stabbing.
-RARITY 25
+GENWEIGHT 40
ASCII ')'
UTF8 ")"
COLOUR iron
WEAPON long sword
PLURAL long swords
DESC The evolution of the cruciform sword in response to ever-heavier plate armour, featuring an unsharpened ricasso to allow its use 'at the half-sword'.
-RARITY 30
+GENWEIGHT 33
ASCII ')'
UTF8 ")"
COLOUR iron
WEAPON windsword
PLURAL windswords
DESC A magic sword imbued with the power of the rushing winds, enabling the wielder to pass safely over such hazards as water, lava, and gaping chasms.
-RARITY 80
+GENWEIGHT 13
ASCII ')'
UTF8 ")"
COLOUR white
WEAPON runesword
PLURAL runeswords
DESC An eerily glowing long sword engraved with many strange runes.
-RARITY 80
+GENWEIGHT 13
ASCII ')'
UTF8 ")"
COLOUR l_cyan
WEAPON mace
PLURAL maces
DESC A flanged lump of iron on an iron haft.
-RARITY 30
+GENWEIGHT 33
ASCII ')'
UTF8 ")"
COLOUR iron
WEAPON spear
PLURAL spears
DESC A blade on a stick.
-RARITY 50
+GENWEIGHT 20
ASCII ')'
UTF8 ")"
COLOUR brown
WEAPON hellglaive
PLURAL hellglaives
DESC A black-hafted polearm with a long, wickedly serrated blade. It thrums with infernal power.
-RARITY 80
+GENWEIGHT 13
ASCII ')'
UTF8 ")"
COLOUR d_grey
WEAPON plague scythe
PLURAL plague sycthes
DESC The few exposed patches of metal on this reeking, filth-encrusted scythe bear an iridescent patina. Only the memory of your battle with its wielder hints at the power it holds.
-RARITY 100
+GENWEIGHT 0
ASCII ')'
UTF8 ")"
COLOUR l_green
WEAPON tormentor's lash
PLURAL tormentor's lashes
DESC A whip of pale, soft leather that crackles with malefic energies.
-RARITY 80
+GENWEIGHT 13
ASCII ')'
UTF8 ")"
COLOUR white
WEAPON death staff
PLURAL death staves
DESC A jet-black staff crowned with a skull.
-RARITY 80
+GENWEIGHT 13
ASCII ')'
UTF8 ")"
COLOUR d_grey
WEAPON staff of fire
PLURAL staves of fire
DESC A jet-black staff with a red glowing gem in its headpiece.
-RARITY 80
+GENWEIGHT 13
ASCII ')'
UTF8 ")"
COLOUR d_grey
WEAPON bow
PLURAL bows
DESC A recurve composite bow.
-RARITY 45
+GENWEIGHT 22
ASCII '('
UTF8 "("
COLOUR brown
WEAPON crossbow
PLURAL crossbows
DESC A crossbow.
-RARITY 70
+GENWEIGHT 14
ASCII '('
UTF8 "("
COLOUR brown
WEAPON thunderbow
PLURAL thunderbows
DESC A recurve composite bow decorated with eldritch signs. Arrows fired with this bow strike with staggering force.
-RARITY 70
+GENWEIGHT 14
ASCII '('
UTF8 "("
COLOUR l_cyan
POTION healing potion
PLURAL healing potions
DESC This magic elixir restores some lost hit points.
-RARITY 10
+GENWEIGHT 100
ASCII '!'
UTF8 "!"
COLOUR l_grey
POTION body potion
PLURAL body potions
DESC This magic elixir will improve your physique.
-RARITY 70
+GENWEIGHT 14
ASCII '!'
UTF8 "!"
COLOUR l_grey
POTION agility potion
PLURAL agility potions
DESC This magic elixir will sharpen your reflexes.
-RARITY 70
+GENWEIGHT 14
ASCII '!'
UTF8 "!"
COLOUR l_grey
POTION restoration potion
PLURAL restoration potions
DESC This magic elixir cures temporary damage to one's abilities.
-RARITY 70
+GENWEIGHT 14
ASCII '!'
UTF8 "!"
COLOUR l_grey
SCROLL teleport scroll
PLURAL teleport scrolls
DESC Reading this scroll will teleport you to a random location.
-RARITY 40
+GENWEIGHT 25
ASCII '?'
UTF8 "?"
COLOUR l_grey
SCROLL fire scroll
PLURAL fire scrolls
DESC Reading this scroll will engulf all nearby creatures (including you) in flames.
-RARITY 30
+GENWEIGHT 33
ASCII '?'
UTF8 "?"
COLOUR l_grey
SCROLL protection scroll
PLURAL protection scrolls
DESC Reading this scroll will dispel any curses afflicting you and protect you from curses for a time.
-RARITY 80
+GENWEIGHT 13
ASCII '?'
UTF8 "?"
COLOUR l_grey
ARMOUR leather armour
PLURAL suits of leather armour
DESC A heavy leather jerkin and breeches, providing some protection.
-RARITY 25
+GENWEIGHT 40
ASCII '['
UTF8 "["
COLOUR brown
ARMOUR chainmail
PLURAL suits of chainmail
DESC A suit of interlocking metal rings, providing better protection than leather.
-RARITY 30
+GENWEIGHT 33
ASCII '['
UTF8 "["
COLOUR iron
ARMOUR plate armour
PLURAL suits of plate armour
DESC A suit of steel plates, providing better protection than chainmail.
-RARITY 40
+GENWEIGHT 25
ASCII '['
UTF8 "["
COLOUR iron
ARMOUR mage armour
PLURAL suits of mage armour
DESC A suit of glowing steel plates bearing enchantments of defence.
-RARITY 70
+GENWEIGHT 14
ASCII '['
UTF8 "["
COLOUR l_cyan
ARMOUR mundane robe
PLURAL mundane robes
DESC A simple woolen robe. It's better protection than your skin, but not by much.
-RARITY 50
+GENWEIGHT 20
ASCII '['
UTF8 "["
COLOUR green
ARMOUR robe of swiftness
PLURAL robes of swiftness
DESC A simple woolen robe that bears a potent enchantment, protecting the wearer and making him unnaturally swift.
-RARITY 70
+GENWEIGHT 14
ASCII '['
UTF8 "["
COLOUR green
ARMOUR robe of shadows
PLURAL robes of shadows
DESC A dusky grey woolen robe that bears an awesome enchantment, protecting the wearer better than steel plate.
-RARITY 90
+GENWEIGHT 10
ASCII '['
UTF8 "["
COLOUR d_grey
ARMOUR dragonhide armour
PLURAL suits of dragonhide armour
DESC The skin of a dragon, formed into a jerkin and breeches; it turns blows like steel plate and turns away flames.
-RARITY 90
+GENWEIGHT 10
ASCII '['
UTF8 "["
COLOUR red
ARMOUR meteoric plate armour
PLURAL suits of meteoric plate armour
DESC This plate armour has been forged out of metal taken from a fallen star.
-RARITY 90
+GENWEIGHT 10
ASCII '['
UTF8 "["
COLOUR iron
ARMOUR sacred chainmail
PLURAL suits of sacred chainmail
DESC This suit of interlocking rings has been consecrated to the gods of the Light.
-RARITY 90
+GENWEIGHT 10
ASCII '['
UTF8 "["
COLOUR white
ARMOUR ragged shift
PLURAL ragged shifts
DESC This sorry-looking collection of rags is all that remains of an imposing armoured dress.
-RARITY 100
+GENWEIGHT 0
ASCII '['
UTF8 "["
COLOUR l_grey
ARMOUR battle ballgown
PLURAL battle ballgowns
DESC Partially armoured dresses such as this are a traditional part of a princess's wardrobe.
-RARITY 95
+GENWEIGHT 5
ASCII '['
UTF8 "["
COLOUR green
ARMOUR imperatrix gown
PLURAL imperatrix gowns
DESC This armoured, enchanted, and elaborately decorated dress would be worthy of an empress regnant.
-RARITY 95
+GENWEIGHT 5
ASCII '['
UTF8 "["
COLOUR purple
ARMOUR foetid vestments
PLURAL sets of foetid vestments
DESC These stained, reeking robes seem to harbour great power despite their half-rotten state.
-RARITY 90
+GENWEIGHT 10
ASCII '['
UTF8 "["
COLOUR l_green
ARMOUR lich's robe
PLURAL lich's robes
DESC Separated from its ancient owner, this robe hums with magical power.
-RARITY 100
+GENWEIGHT 0
ASCII '['
UTF8 "["
COLOUR purple
ARMOUR infernite armour
PLURAL suits of infernite armour
DESC Crafted by Verant, or perhaps one of his hellish apprentices, after his fall from the light, this armour was ancient before your ancestors even came to the lands called Söťerek.
-RARITY 100
+GENWEIGHT 0
ASCII '['
UTF8 "["
COLOUR red
ARMOUR set of ribbons
PLURAL sets of ribbons
DESC These ribbons, arranged as if to form an alleged garment, make your fingers tingle with magic.
-RARITY 90
+GENWEIGHT 10
ASCII '['
UTF8 "["
COLOUR l_purple
RING regeneration ring
PLURAL regeneration rings
DESC This magical ring increases the wearer's healing rate, but also increases the rate at which they must consume food.
-RARITY 70
+GENWEIGHT 14
ASCII '='
UTF8 "="
COLOUR l_grey
POWER 0
POWER2 0
DEPTH 1
+REGEN
RING fire ring
PLURAL fire rings
DESC This magical ring protects the wearer from mundane and magical fire, and imbues their blows in combat with the power of fire.
-RARITY 50
+GENWEIGHT 20
ASCII '='
UTF8 "="
COLOUR l_grey
RING vampire ring
PLURAL vampire rings
DESC This magical ring protects the wearer from necromantic energies, and imbues their blows in combat with such energies as well.
-RARITY 90
+GENWEIGHT 10
ASCII '='
UTF8 "="
COLOUR l_grey
RING frost ring
PLURAL frost rings
DESC This magical ring protects the wearer from mundane and magical cold, and imbues their blows in combat with the power of cold. Rumour suggests it might also allow walking on water.
-RARITY 40
+GENWEIGHT 25
ASCII '='
UTF8 "="
COLOUR l_grey
RING teleport ring
PLURAL teleport rings
DESC This magical ring allows the wearer to teleport for a modest cost in nutrition.
-RARITY 70
+GENWEIGHT 14
ASCII '='
UTF8 "="
COLOUR l_grey
RING slime ring
PLURAL slime rings
DESC This magical ring, tarnished and filth-spotted, protects its wearer from a poison and pestilence, whether magical or natural.
-RARITY 75
+GENWEIGHT 12
ASCII '='
UTF8 "="
COLOUR l_grey
RING protection ring
PLURAL protection rings
DESC This magical ring, engraved with the sigil of Verant the Smith, substantially reduces the severity of all injuries its wearer suffers.
-RARITY 75
+GENWEIGHT 12
ASCII '='
UTF8 "="
COLOUR l_grey
RING imperial seal
PLURAL imperial seals
DESC This rarest and most ancient of magical rings was forged in the days before the Archon Inferni was banished to the Abyss. Of the nature of its power, there are not even rumours.
-RARITY 100
+GENWEIGHT 0
ASCII '='
UTF8 "="
COLOUR purple
FOOD iron ration
PLURAL iron rations
DESC A parcel of hardtack and beef jerky. Dull but nutritious.
-RARITY 75
+GENWEIGHT 12
ASCII '%'
UTF8 "%"
COLOUR brown
FOOD parcel of dried fruit
PLURAL parcels of dried fruit
DESC A parcel of mixed dried fruit. It sure beats hardtack and beef jerky.
-RARITY 75
+GENWEIGHT 12
ASCII '%'
UTF8 "%"
COLOUR yellow
FOOD round of elven waybread
PLURAL rounds of elven waybread
DESC A tasty, filling, nutritious piece of elven waybread.
-RARITY 85
+GENWEIGHT 11
ASCII '%'
UTF8 "%"
COLOUR white
FOOD devil spleen
PLURAL devil spleens
DESC A weirdly pulsing organ ripped from the torso of a devil.
-RARITY 100
+GENWEIGHT 0
ASCII '%'
UTF8 "%"
COLOUR l_red
CARRION corpse
PLURAL corpses
DESC The remains of a defeated enemy.
-RARITY 100
+GENWEIGHT 0
ASCII '&'
UTF8 "&"
COLOUR l_grey
ascii '@'
utf8 "@"
desc A typical morally stunted specimen of the dungeoneering class.
-rarity 100
+genweight 0
power 1
hp 1
mtohit 0
utf8 "n"
desc A small, not very threatening, vaguely lizard-like creature.
colour red
-rarity 20
+genweight 50
power 1
hp 3
mtohit 0
desc A large and bad-tempered rodent not noted for its dental hygeine.
colour brown
power 1
-rarity 15
+genweight 60
hp 4
mhit 0
mdam 2
utf8 "c"
desc A hungry canid, probably weighing nearly as much as you do in your skin.
colour brown
-rarity 30
+genweight 30
power 6
hp 20
mhit 8
ascii 'S'
utf8 "S"
colour red
-rarity 20
+genweight 50
power 6
hp 15
mhit 10
ascii 't'
utf8 "t"
colour brown
-rarity 30
+genweight 30
power 1
hp 8
mhit 5
ascii 't'
utf8 "t"
colour yellow
-rarity 20
+genweight 50
power 3
hp 15
mhit 6
ascii 'h'
utf8 "h"
colour green
-rarity 30
+genweight 30
power 9
hp 40
mhit 6
ascii 'f'
utf8 "f"
colour red
-rarity 40
+genweight 25
power 12
hp 60
mhit 30
ascii 'f'
utf8 "f"
colour l_red
-rarity 30
+genweight 30
power 15
hp 80
mhit 25
ascii 'f'
utf8 "f"
colour l_purple
-rarity 30
+genweight 30
power 18
hp 120
mhit 30
ascii 'g'
utf8 "g"
colour brown
-rarity 20
+genweight 50
power 1
hp 6
mhit 1
ascii 'e'
utf8 "e"
colour l_grey
-rarity 40
+genweight 25
power 3
hp 15
mhit 10
ascii 'T'
utf8 "T"
colour green
-rarity 20
+genweight 50
power 12
hp 80
mhit 15
ascii 'H'
utf8 "H"
colour brown
-rarity 20
+genweight 50
power 21
hp 80
mhit 15
ascii 'H'
utf8 "H"
colour l_grey
-rarity 80
+genweight 13
power 25
hp 160
mhit 20
ascii 'w'
utf8 "w"
colour blue
-rarity 80
+genweight 13
power 12
hp 40
mhit 10
ascii 'w'
utf8 "w"
colour l_blue
-rarity 80
+genweight 13
power 24
hp 80
mhit 15
ascii 'z'
utf8 "z"
colour l_grey
-rarity 25
+genweight 40
depth 3
hp 30
mhit 2
ascii 'W'
utf8 "W"
colour white
-rarity 25
+genweight 40
depth 12
hp 40
mhit 25
ascii 'L'
utf8 "L"
colour l_grey
-rarity 70
+genweight 14
power 15
hp 70
mhit 15
ascii 'L'
utf8 "L"
colour purple
-rarity 60
+genweight 15
power 30
hp 150
mhit 30
ascii 'V'
utf8 "V"
colour red
-rarity 55
+genweight 18
power 18
hp 70
mhit 25
ascii '4'
utf8 "4"
colour red
-rarity 60
+genweight 15
power 9
hp 20
mhit 10
ascii '3'
utf8 "3"
colour red
-rarity 60
+genweight 15
power 18
hp 40
mhit 25
ascii '2'
utf8 "2"
colour l_green
-rarity 65
+genweight 16
power 27
hp 120
mhit 30
ascii '1'
utf8 "1"
colour l_green
-rarity 90
+genweight 10
power 30
hp 250
mhit 20
ascii '1'
utf8 "1"
colour purple
-rarity 90
+genweight 10
power 30
hp 150
mhit 50
ascii '1'
utf8 "1"
colour iron
-rarity 90
+genweight 10
power 30
hp 300
mhit 30
ascii 'C'
utf8 "C"
colour brown
-rarity 30
+genweight 30
power 9
hp 40
mhit 15
ascii 'E'
utf8 "E"
colour white
-rarity 50
+genweight 20
power 6
hp 40
mhit 10
ascii 'E'
utf8 "E"
colour red
-rarity 50
+genweight 20
power 15
hp 100
mhit 15
ascii 'D'
utf8 "D"
colour red
-rarity 50
+genweight 20
power 15
hp 80
mhit 20
colour l_cyan
ascii 'D'
utf8 "D"
-rarity 60
+genweight 15
power 18
hp 80
mhit 20
#define DISPLAY_NC_CC
#include "victrix-abyssi.hh"
-#include "monsters.hh"
-#include "objects.hh"
+#include "display.hh"
+#include "text.hh"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
//#define DEBUG_TO_STDERR
#define DISP_NC_RADIUS (10)
#define DISP_NC_SIDE (DISP_NC_RADIUS * 2 + 1)
+#define MSG_MIN_WIDTH (58)
/* Prototypes for static funcs */
static cchar_t const *object_char(int po_ref);
static void reset_inventory_message(void);
static void hide_inv(void);
static void show_inv(void);
-static void update_inv(enum poclass_num filter);
-static int inv_select(enum poclass_num filter, char const *action, int accept_blank);
+static void update_inv(Poclass_num filter);
+static int inv_select(Poclass_num filter, char const *action, int accept_blank);
static void farlook(void);
/*! \brief For showing the player's status summary */
print_msg(Msg_prio::Bug, "\nBUG: no role selected.\n\n");
break;
case Role_princess:
- print_msg("\nYou are the rightful heir to your mother's throne,\n");
- print_msg("and have been banished to this grim abyssal\n");
- print_msg("realm by your father's treacherous bastard. The\n");
- print_msg("power of the royal house's magic echoes in your\n");
- print_msg("soul, though you have little hope of directing it\n");
- print_msg("well without the proper talismans.\n\n");
+ print_msg("\nYou are the rightful heir to your mother's throne, "
+ "and have been banished to this grim abyssal "
+ "realm by your father's treacherous bastard. The "
+ "power of the royal house's magic echoes in your "
+ "soul, though you have little hope of directing it "
+ "well without the proper talismans.\n\n");
break;
case Role_demon_hunter:
- print_msg("\nWhile fighting the members of a devil-cult, you were\n");
- print_msg("banished to this strange, shadowy realm by the foul\n");
- print_msg("sorcery of the cult leader. You imagine your training\n");
- print_msg("will serve you well here, for what could be a more\n");
- print_msg("obvious home for demons than a realm to which a cultist\n");
- print_msg("would banish you?\n\n");
+ print_msg("\nWhile fighting the members of a devil-cult, you were "
+ "banished to this strange, shadowy realm by the foul "
+ "sorcery of the cult leader. You imagine your training "
+ "will serve you well here, for what could be a more "
+ "obvious home for demons than a realm to which a cultist "
+ "would banish you?\n\n");
break;
case Role_thanatophile:
- print_msg("\nPursuing a notorious necromancer, you stumbled on a\n");
- print_msg("magical trap in his lair and found yourself banished\n");
- print_msg("to this strange, shadowy realm. The austere arts of\n");
- print_msg("beloved Death will serve well should you encounter the\n");
- print_msg("walking dead in this terrible place.\n\n");
+ print_msg("\nPursuing a notorious necromancer, you stumbled on a "
+ "magical trap in his lair and found yourself banished "
+ "to this strange, shadowy realm. The austere arts of "
+ "beloved Death will serve well should you encounter the "
+ "walking dead in this terrible place.\n\n");
break;
}
}
static char const *tips[] = {
"Pay attention to your hit points.",
"Magic rings never wear out; don't bother carrying duplicates.",
- "Demon hunters cannot use demonic equipment; it is unclean.",
+ //"Demon hunters cannot use demonic equipment; it is unclean.",
"Chasms, lava, and water can all be crossed using suitable gear.",
"Dying with a healing potion in your inventory is embarrassing.",
"Fire scrolls are a powerful weapon if you resist fire.",
"Corpses are not a food source.",
"Corpses blessed by Death do not rise again to trouble the living.",
"The PRNG is a dispassionate algorithm, incapable of love or hate.",
- "Life is not fair, and neither is Obumbrata et Velata.",
+ "Life is not fair, and neither is Victrix Abyssi.",
"Most weapons and armour are consumable resources.",
"More tips wanted. Send suggestions to the author, please."
};
*
* Yes, this architecture does mean we only support one display subsystem at
* compile time.
- *
- * \todo Rename this and put appropriate glue elsewhere to make it work
*/
-int launch_user_interface(void)
+int launch_user_interface(int argc, char **argv)
{
int i;
int j;
front_buffer[i][j] = back_buffer[i][j] = &blank_tile;
}
}
- /* OK. We want a 21x21 viewport (player at centre), a 21x58 message
+ /* OK. We want a 21x21 viewport (player at centre), a 21xMSG_MIN_WIDTH message
* window, and a 2x80 status line. */
status_window = newwin(2, 80, DISP_HEIGHT + 1, 0);
status_panel = new_panel(status_window);
world_window = newwin(DISP_HEIGHT, DISP_WIDTH, 0, 0);
world_panel = new_panel(world_window);
- message_window = newwin(DISP_HEIGHT, 58, 0, DISP_WIDTH + 1);
+ message_window = newwin(DISP_HEIGHT, MSG_MIN_WIDTH, 0, DISP_WIDTH + 1);
message_panel = new_panel(message_window);
- inventory_window = newwin(DISP_HEIGHT, 58, 0, DISP_WIDTH + 1);
+ inventory_window = newwin(DISP_HEIGHT, MSG_MIN_WIDTH, 0, DISP_WIDTH + 1);
inventory_panel = new_panel(inventory_window);
fullscreen_window = newwin(24, 80, 0, 0);
fullscreen_panel = new_panel(fullscreen_window);
return strlen(buffer);
}
+static void print_and_discard(char *s)
+{
+ char *s2 = cstr_line_format(s, MSG_MIN_WIDTH, nullptr);
+ waddstr(message_window, s2);
+ display_update();
+ free(s);
+ free(s2);
+}
+
/*! \brief Print some text in the message window
*
* Calling print_msg() prints formatted text (printf-style) to the message
void print_msg(char const *fmt, ...)
{
va_list ap;
+ int i;
+ char *s;
va_start(ap, fmt);
- vw_printw(message_window, fmt, ap);
+ i = vasprintf(&s, fmt, ap);
va_end(ap);
#ifdef DEBUG_TO_STDERR
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
+ fwrite(s, 1, i, stderr);
#endif
- display_update();
+ print_and_discard(s);
}
/*! \brief Print some text in the message window (priority variant)
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);
+#ifdef DEBUG_TO_STDERR
+ fwrite(s, 1, i, stderr);
+#endif
switch (prio)
{
case Msg_prio::Low:
wattr_set(message_window, colour_data[Gcol_prio_bug].attr, colour_data[Gcol_prio_bug].cpair, nullptr);
break;
}
- if (j > 50)
- {
- /* TODO catch this */
- }
- waddstr(message_window, s);
-#ifdef DEBUG_TO_STDERR
- fwrite(s, 1, j, stderr);
-#endif
+ print_and_discard(s);
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
* \param filter Class of objects to highlight; (POCLASS_NONE to highlight all)
* \todo Switch to using a function pointer + private argument for filtering
*/
-static void update_inv(enum poclass_num filter)
+static void update_inv(Poclass_num filter)
{
int i;
char inv_line[60];
* \param accept_blank If true, the pseudo-item "nothing" can be selected
* \todo Take function pointer and private arg for smarter filtering
*/
-static int inv_select(enum poclass_num filter, char const *action, int accept_blank)
+static int inv_select(Poclass_num filter, char const *action, int accept_blank)
{
int selection;
int ch;
return SLOT_CANCEL;
}
wattr_set(inventory_window, colour_data[Gcol_l_grey].attr, colour_data[Gcol_l_grey].cpair, nullptr);
- snprintf(msg, 58, "What do you want to %s?\n", action);
+ snprintf(msg, MSG_MIN_WIDTH, "What do you want to %s?\n", action);
set_inventory_message(msg, accept_blank);
update_inv(filter);
wattr_set(inventory_window, colour_data[Gcol_l_grey].attr, colour_data[Gcol_l_grey].cpair, nullptr);
* \param pstep Location to store direction's Offset value in
* \todo Consider making this accommodate up/down/self as well as compass directions
*/
-Pass_fail select_dir(Offset *pstep)
+Success select_dir(Offset *pstep)
{
int ch;
bool done = false;
case '\x1b':
case ' ':
print_msg("Cancelled.\n");
- return You_fail; /* cancelled. */
+ return Success::fail; /* cancelled. */
default:
print_msg("Bad direction (use movement keys).\n");
print_msg("[Press ESC or space to cancel.]\n");
break;
}
}
- return You_pass;
+ return Success::pass;
}
/*! \brief Read the player's next command
void get_player_action(Action *act)
{
int ch;
- Pass_fail pf;
+ Success pf;
Offset step;
while (1)
{
{
act->cmd = ATTACK;
pf = select_dir(&step);
- if (pf != You_fail)
+ if (pf != Success::fail)
{
act->details[0] = step.y;
act->details[1] = step.x;
{
print_msg("You are not wearing a ring.\n");
}
- else if (unequip_safety_check(BONUS_RING, Noise_std) == You_pass)
+ else if (unequip_safety_check(BONUS_RING, Noise_std) == Success::pass)
{
act->cmd = REMOVE_RING;
return;
Bug
};
-void print_msg(char const *fmt, ...);
-void print_msg(Msg_prio prio, char const *fmt, ...);
+#define PRINTMSG_FMT_ATTRS(a,b) __attribute__((format(__printf__, (a), (b) )))
+
+void print_msg(char const *fmt, ...) PRINTMSG_FMT_ATTRS(1,2);
+void print_msg(Msg_prio prio, char const *fmt, ...) PRINTMSG_FMT_ATTRS(2,3);
int read_input(char *buffer, int length);
void print_help(void);
-int launch_user_interface(void);
+// the Qt front-end will need argc and argv
+int launch_user_interface(int argc, char **argv);
void display_update(void);
int display_shutdown(void);
void newsym(Coord c);
void touch_back_buffer(void);
void get_player_action(Action *act);
-Pass_fail select_dir(Offset *pstep);
+Success select_dir(Offset *pstep);
int getYN(char const *msg);
int getyn(char const *msg);
void press_enter(void);
/*! \brief Excavate a "claustrophobia" level
*
- * "Claustrophobia" levels
- *
- * \todo Implement.
+ * "Claustrophobia" levels have a BSP-generated room layout.
*/
void build_level_claustrophobia(Level *l)
{
--- /dev/null
+/*! \file levtheme.cc
+ * \brief Generation of object/monster tables based on level theme
+ */
+
+/* Copyright 2014 Martin Read
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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.
+ */
+
+#include "victrix-abyssi.hh"
+#include "mapgen.hh"
+
+static void normal_theme_monster_table_generator(Mon_gen_table *table)
+{
+ table->vec.clear();
+ for (int i = 0; i < NUM_OF_PERMONS; ++i)
+ {
+ Mon_row r = { i, permons[i].genweight };
+ if (permons[i].power <= depth)
+ {
+ if (permons[i].power <= (depth / 2))
+ {
+ r.weight /= 2;
+ if (permons[i].power <= (depth / 4))
+ {
+ r.weight /= 2;
+ }
+ }
+ table->total_weight += r.weight;
+ table->vec.push_back(r);
+ }
+ }
+ if (table->vec.empty())
+ {
+ throw Game_levgen_excep("No monsters available for NORMAL theme at this depth");
+ }
+}
+
+static void dragon_theme_monster_table_generator(Mon_gen_table *table)
+{
+ table->vec.clear();
+ for (int i = 0; i < NUM_OF_PERMONS; ++i)
+ {
+ Mon_row r = { i, permons[i].genweight };
+ if (permons[i].power <=depth)
+ {
+ if (permons[i].power <=(depth / 2))
+ {
+ r.weight /= 2;
+ if (permons[i].power <=(depth / 4))
+ {
+ r.weight /= 2;
+ }
+ }
+ table->total_weight += r.weight;
+ table->vec.push_back(r);
+ }
+ }
+ if (table->vec.empty())
+ {
+ throw Game_levgen_excep("No monsters available for DRAGON theme at this depth");
+ }
+}
+
+static void demon_theme_monster_table_generator(Mon_gen_table *table)
+{
+ table->vec.clear();
+ for (int i = 0; i < NUM_OF_PERMONS; ++i)
+ {
+ Mon_row r = { i, permons[i].genweight };
+ if ((permons[i].power <=depth) && pmon_is_demonic(i))
+ {
+ if (permons[i].power <=(depth / 2))
+ {
+ r.weight /= 2;
+ if (permons[i].power <=(depth / 4))
+ {
+ r.weight /= 2;
+ }
+ }
+ table->total_weight += r.weight;
+ table->vec.push_back(r);
+ }
+ }
+ if (table->vec.empty())
+ {
+ throw Game_levgen_excep("No monsters available for DEMON theme at this depth");
+ }
+}
+
+static void undead_theme_monster_table_generator(Mon_gen_table *table)
+{
+ table->vec.clear();
+ for (int i = 0; i < NUM_OF_PERMONS; ++i)
+ {
+ Mon_row r = { i, permons[i].genweight };
+ if ((permons[i].power <=depth) && pmon_is_undead(i))
+ {
+ if (permons[i].power <=(depth / 2))
+ {
+ r.weight /= 2;
+ if (permons[i].power <=(depth / 4))
+ {
+ r.weight /= 2;
+ }
+ }
+ table->total_weight += r.weight;
+ table->vec.push_back(r);
+ }
+ }
+ if (table->vec.empty())
+ {
+ throw Game_levgen_excep("No monsters available for UNDEAD theme at this depth");
+ }
+}
+
+Montable_gen_func themed_montable_funcs[NUM_THEMES] = {
+ normal_theme_monster_table_generator,
+ dragon_theme_monster_table_generator,
+ demon_theme_monster_table_generator,
+ undead_theme_monster_table_generator,
+};
+
+/*! \brief generate object table for "normal" theme
+ *
+ * \todo implement
+ */
+static void normal_theme_object_table_generator(Obj_gen_table *table)
+{
+ table->vec.clear();
+ for (int i = 0; i < NUM_OF_PERMOBJS; ++i)
+ {
+ Obj_row r = { i, permobjs[i].genweight };
+ if (permobjs[i].depth <= depth)
+ {
+ table->total_weight += r.weight;
+ table->vec.push_back(r);
+ }
+ }
+ if (table->vec.empty())
+ {
+ throw Game_levgen_excep("No objects available for NORMAL theme at this depth");
+ }
+}
+
+/*! \brief generate object table for "dragon" theme
+ *
+ * \todo implement
+ */
+static void dragon_theme_object_table_generator(Obj_gen_table *table)
+{
+ normal_theme_object_table_generator(table);
+}
+
+/*! \brief generate object table for "demon" theme
+ *
+ * \todo implement
+ */
+static void demon_theme_object_table_generator(Obj_gen_table *table)
+{
+ normal_theme_object_table_generator(table);
+}
+
+/*! \brief generate object table for "undead" theme
+ *
+ * \todo implement
+ */
+static void undead_theme_object_table_generator(Obj_gen_table *table)
+{
+ normal_theme_object_table_generator(table);
+}
+
+Objtable_gen_func themed_objtable_funcs[NUM_THEMES] = {
+ normal_theme_object_table_generator,
+ dragon_theme_object_table_generator,
+ demon_theme_object_table_generator,
+ undead_theme_object_table_generator,
+};
+
+/* levtheme.cc */
+// vim:cindent:expandtab:ts=8:sw=4
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
serialize_int(fp, obj.durability);
}
+/*! \brief Read a monster selection table */
+static void deserialize_mongen_table(FILE *fp, Mon_gen_table *table)
+{
+ uint32_t i;
+ uint32_t j;
+ deserialize_uint32(fp, &i);
+ table->vec.clear();
+ table->vec.reserve(i);
+ table->total_weight = 0;
+ for (j = 0; j < i; ++j)
+ {
+ Mon_row mr;
+ deserialize_int32(fp, &mr.pm);
+ deserialize_uint32(fp, &mr.weight);
+ table->total_weight += mr.weight;
+ table->vec.push_back(mr);
+ }
+}
+
+/*! \brief Write a monster selection table */
+static void serialize_mongen_table(FILE *fp, Mon_gen_table const& table)
+{
+ serialize_uint32(fp, (uint32_t) table.vec.size());
+ for (auto iter = table.vec.begin(); iter != table.vec.end(); ++iter)
+ {
+ serialize_int32(fp, iter->pm);
+ serialize_uint32(fp, iter->weight);
+ }
+}
+
/*! \brief Read the player */
static void deserialize_player(FILE *fp, Player *player)
{
}
/*! \brief Deserialize a Level */
-void deserialize_level(FILE *fp, Level *l)
+static void deserialize_level(FILE *fp, Level *l)
{
uint32_t tmp;
uint32_t tmp_pair[2];
l->theme = Level_theme(ntohl(tmp));
wrapped_fread(&tmp, sizeof tmp, 1, fp);
l->layout = Level_layout(ntohl(tmp));
- wrapped_fread(&tmp, sizeof tmp, 1, fp);
- l->chunks_high = ntohl(tmp);
- wrapped_fread(&tmp, sizeof tmp, 1, fp);
- l->chunks_wide = ntohl(tmp);
+ deserialize_uint32(fp, &(l->threat));
+ deserialize_uint32(fp, &(l->chunks_high));
+ deserialize_uint32(fp, &(l->chunks_wide));
initialize_chunks(l, l->chunks_high, l->chunks_wide, false);
do
{
deserialize_chunk(fp, l->chunks[i][j]);
}
while (1);
+ deserialize_mongen_table(fp, &(l->summons));
}
/*! \brief Serialize a Level */
-void serialize_level(FILE *fp, Level const *l)
+static void serialize_level(FILE *fp, Level const *l)
{
uint32_t tmp;
uint32_t tmp_pair[2];
fwrite(&tmp, sizeof tmp, 1, fp);
tmp = htonl(l->layout);
fwrite(&tmp, sizeof tmp, 1, fp);
- tmp = htonl(l->chunks_high);
- fwrite(&tmp, sizeof tmp, 1, fp);
- tmp = htonl(l->chunks_wide);
- fwrite(&tmp, sizeof tmp, 1, fp);
+ serialize_uint32(fp, l->threat);
+ serialize_uint32(fp, l->chunks_high);
+ serialize_uint32(fp, l->chunks_wide);
for (i = 0; i < l->chunks_high; ++i)
{
tmp_pair[0] = htonl(i);
}
tmp_pair[0] = tmp_pair[1] = ~0u;
fwrite(tmp_pair, sizeof tmp_pair[0], 2, fp);
+ serialize_mongen_table(fp, l->summons);
}
/*! \brief Save the game; set game_finished flag if successful */
serialize_uint32(fp, SAVEFILE_MAGIC_NUMBER);
serialize_uint32(fp, MAJVERS);
serialize_uint32(fp, MINVERS);
- serialize_int(fp, depth);
+ serialize_uint32(fp, depth);
serialize_int(fp, game_tick);
serialize_objhandle(fp, first_free_obj_handle);
serialize_monhandle(fp, first_free_mon_handle);
check_majvers(tmp);
deserialize_uint32(fp, &tmp);
check_minvers(tmp);
- deserialize_int(fp, &depth);
+ deserialize_uint32(fp, &depth);
deserialize_int(fp, &game_tick);
deserialize_objhandle(fp, &first_free_obj_handle);
if (first_free_obj_handle == 0)
#include "victrix-abyssi.hh"
#include "combat.hh"
-#include "objects.hh"
-#include "monsters.hh"
-#include "map.hh"
-#include "player.hh"
+#include "display.hh"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
}
/*! \brief main() */
-int main(void)
+int main(int argc, char *argv[])
{
load_config();
rng_init();
- launch_user_interface();
+ launch_user_interface(argc, argv);
if (!game_finished)
{
main_loop();
#include <string.h>
Level lvl;
-int depth;
+uint32_t depth;
Decal_desc decal_props[NUM_DECALS] =
{
void make_new_level(void)
{
- build_level();
+ Level_key lk = { 0, (int16_t) depth };
+ lvl.build(lk);
populate_level();
notify_change_of_depth();
inject_player(&lvl, lvl.self.naive_prev());
return c;
}
-/*! \brief Entry point for level generation.
- *
- * \todo Maybe implement support for Lua-based level generators.
- */
-void build_level(void)
+/*! \brief Standard layout selector table for the "main dungeon" */
+Layout_selector layout_selectors[] = {
+ { 8, 1, 7, LAYOUT_CAVE_SHRINE },
+ { 0, 1, 6, LAYOUT_CAVE_PITS },
+ { 0, 1, 5, LAYOUT_CAVE_INTRUSIONS },
+ { 0, 1, 1, LAYOUT_CLASSIC_CAVE },
+ { 0, 0, 0, NO_LAYOUT }
+};
+
+/*! \brief Select a level layout based on a level key and a threat rating */
+Level_layout dungeon_level_layout(Level_key lk, uint32_t threat)
{
- int theme_roll;
- lvl.self.depth = (int16_t) depth;
- lvl.self.dungeon = 0;
- lvl.stairs.clear();
- rng.extract_serialization(saved_state_buffer, saved_state_size);
- theme_roll = zero_die(depth + 50);
- if (!zero_die(6))
- {
- lvl.layout = LAYOUT_CAVE_PITS;
- }
- else if (!zero_die(5))
+ Level_layout layout = NO_LAYOUT;
+ for (int i = 0; layout_selectors[i].layout != NO_LAYOUT; ++i)
{
- lvl.layout = LAYOUT_CAVE_INTRUSIONS;
- }
- else if ((depth > 8) && !zero_die(6))
- {
- lvl.layout = LAYOUT_CAVE_SHRINE;
+ if (layout_selectors[i].threat <= threat)
+ {
+ if (zero_die(layout_selectors[i].sides) <
+ layout_selectors[i].tgt_num)
+ {
+ layout = layout_selectors[i].layout;
+ break;
+ }
+ }
}
- else
+ if (layout == NO_LAYOUT)
{
- lvl.layout = LAYOUT_CLASSIC_CAVE;
+ throw Game_levgen_excep("dungeon_level_layout failed to select valid layout");
}
- if ((theme_roll < 50) || (depth < 10))
+ return layout;
+}
+
+/*! \brief Select a level theme based on a level key and a threat rating */
+Level_theme dungeon_level_theme(Level_key lk, uint32_t threat)
+{
+ Level_theme theme = NO_THEME;
+ int theme_roll = zero_die(std::min(50 + threat, 90u));
+ if ((theme_roll < 50) || (threat < 10))
{
- lvl.theme = THEME_NORMAL; /* no restrictions */
+ theme = THEME_NORMAL; /* no restrictions */
}
else if (theme_roll < 60)
{
- lvl.theme = THEME_UNDEAD;
+ theme = THEME_UNDEAD;
}
else if (theme_roll < 80)
{
- lvl.theme = THEME_DRAGONS;
+ theme = THEME_DRAGONS;
}
- else if (theme_roll < 90)
+ else
{
- lvl.theme = THEME_DEMONS;
+ theme = THEME_DEMONS;
}
- switch (lvl.layout)
+ if (theme == NO_THEME)
{
- case LAYOUT_CAVE_SHRINE:
- build_level_shrine(&lvl);
- break;
- case LAYOUT_CAVE_INTRUSIONS:
- build_level_intrusions(&lvl);
- break;
- case LAYOUT_DUNGEONBASH:
- build_level_dungeonbash(&lvl);
- break;
- case LAYOUT_CLASSIC_CAVE:
- build_level_cave(&lvl);
- break;
- case LAYOUT_CAVE_PITS:
- if (depth > 5)
- {
- if (!zero_die(3))
- {
- build_level_pits(&lvl, LAVA);
- }
- else if (!zero_die(2))
- {
- build_level_pits(&lvl, WATER);
- }
- else
- {
- build_level_pits(&lvl, CHASM);
- }
- }
- else
- {
- build_level_pits(&lvl, CHASM);
- }
- break;
- case LAYOUT_CLAUSTROPHOBIA:
- build_level_claustrophobia(&lvl);
- break;
+ throw Game_levgen_excep("dungeon_level_theme failed to select valid theme");
}
+ return theme;
+}
+
+/*! Table of level excavation functions */
+Levgen_build_func level_build_funcs[NUM_LAYOUTS] =
+{
+ build_level_cave,
+ build_level_intrusions,
+ build_level_shrine,
+ build_level_pits,
+ build_level_dungeonbash,
+ build_level_claustrophobia
+};
+
+/*! \brief Entry point for level generation.
+ *
+ * \todo Maybe implement support for Lua-based level generators.
+ */
+void Level::build(Level_key where)
+{
+ self = where;
+ stairs.clear();
+ threat = self.depth;
+ layout = dungeon_level_layout(self, threat);
+ theme = dungeon_level_theme(self, threat);
+ level_build_funcs[theme](this);
}
/*! \brief Excavation function for use with random walks */
}
/*! \brief Get a valid square to generate a monster on */
-Pass_fail get_levgen_mon_floor(Level *l, Coord *c)
+Success get_levgen_mon_floor(Level *l, Coord *c)
{
int cell_try;
Coord t;
}
if (t == Nowhere)
{
- return You_fail;
+ return Success::fail;
}
*c = t;
- return You_pass;
+ return Success::pass;
}
-/*! \brief Populate the level! */
+/*! \brief Populate the level!
+ *
+ * \todo be more flexible about what monster table to use for summons
+ */
void populate_level(void)
{
- int i;
- Pass_fail pf;
+ Success pf;
Coord c;
- int ic;
+ uint32_t item_count = std::min(3u + depth, 40u);
+ uint32_t monster_count = std::min(10u + depth, 40u);
+ uint32_t pm;
+ Mon_gen_table mtbl;
+ Obj_gen_table otbl;
+ themed_montable_funcs[lvl.theme](&mtbl);
+ themed_objtable_funcs[lvl.theme](&otbl);
+ lvl.summons = mtbl;
/* Generate some random monsters */
- for (i = 0; i < (10 + depth); i++)
+ for (uint32_t i = 0; i < monster_count; i++)
{
pf = get_levgen_mon_floor(&lvl, &c);
- if (pf == You_fail)
+ if (pf == Success::fail)
{
continue;
}
- create_mon(NO_PMON, c);
- }
- ic = 3 + depth;
- if (ic > 40)
- {
- /* Never create more than 40 items. */
- ic = 40;
+ pm = mtbl.get_random_entry();
+ create_mon(pm, c);
}
/* Generate some random treasure */
- for (i = 0; i < ic; i++)
+ for (uint32_t i = 0; i < item_count; i++)
{
pf = get_levgen_mon_floor(&lvl, &c);
- if (pf == You_fail)
+ if (pf == Success::fail)
{
continue;
}
- create_obj(NO_POBJ, 1, 0, c);
+ int32_t po = otbl.get_random_entry();
+ create_obj(po, 1, 0, c);
}
}
notify_fov();
}
+int32_t Mon_gen_table::get_random_entry(void)
+{
+ uint32_t roll = zero_die(total_weight);
+ for (size_t i = 0; i < vec.size(); ++i)
+ {
+ if (roll < vec[i].weight)
+ {
+ return vec[i].pm;
+ }
+ roll -= vec[i].weight;
+ }
+ return NO_PMON;
+}
+
+int32_t Obj_gen_table::get_random_entry(void)
+{
+ uint32_t roll = zero_die(total_weight);
+ for (size_t i = 0; i < vec.size(); ++i)
+ {
+ if (roll < vec[i].weight)
+ {
+ return vec[i].po;
+ }
+ roll -= vec[i].weight;
+ }
+ return NO_POBJ;
+}
+
/*! \brief Description of terrain types */
Terrain_props terrain_props[NUM_TERRAINS] =
{
#endif
#include <deque>
-/* XXX enum terrain_num */
-enum Terrain {
- WALL = 0, MASONRY_WALL, AMETHYST_WALL, IRON_WALL, SKIN_WALL, BONE_WALL, DOOR, FLOOR, AMETHYST_FLOOR, IRON_FLOOR, SKIN_FLOOR, BONE_FLOOR, ALTAR, STAIRS_UP, STAIRS_DOWN, PORTAL_LANDING, PORTAL_ONWARD, CHASM, LAVA, WATER
-};
-#define MAX_TERRAIN (WATER)
-#define NUM_TERRAINS (1 + MAX_TERRAIN)
#define MAPFLAG_EXPLORED 0x00000001
#define MAPFLAG_HARDWALL 0x00000002
+/*! \brief Monster population theme
+ *
+ * \todo Actually do something with these values
+ */
enum Level_theme {
- THEME_NORMAL = 0, THEME_DRAGONS, THEME_DEMONS, THEME_UNDEAD
+ NO_THEME = -1, THEME_NORMAL = 0, THEME_DRAGONS, THEME_DEMONS, THEME_UNDEAD
};
+#define LAST_THEME (THEME_UNDEAD)
+#define NUM_THEMES (1 + LAST_THEME)
+/*! \brief Level excavation algorithm selector */
enum Level_layout
{
+ NO_LAYOUT = -1,
LAYOUT_CLASSIC_CAVE = 0,
LAYOUT_CAVE_INTRUSIONS, /* the cave has hardened intrusions */
LAYOUT_CAVE_SHRINE, /* the cave contains a shrine */
LAYOUT_DUNGEONBASH, /* dungeonbash-style room grid */
LAYOUT_CLAUSTROPHOBIA
};
+#define LAST_LAYOUT LAYOUT_CLAUSTROPHOBIA
+#define NUM_LAYOUTS (1 + LAST_LAYOUT)
#define NO_REGION 0xffffffffu
#define SHRINE_HEIGHT 11
#define SHRINE_WIDTH 11
-//! Layout data for "shrines"
+/*! \brief Layout data for "shrines"
+ *
+ * A shrine is a room with a predefined rotatable layout.
+ */
struct shrine
{
bool used;
char const *grid[SHRINE_HEIGHT];
};
-//! Property structure for terrain types
-struct Terrain_props
-{
- char const *name; //!< UTF-8 encoded English name
- char ascii; //!< ASCII symbol
- char const * unicode; //!< UTF-8 encoded Unicode symbol
- Gamecolour colour; //!< colour to use in terminal-like environments
- uint32_t flags; //!< Bitmask composed using TFLAG_* macros.
-};
-
-//! Array of terrain properties
-extern Terrain_props terrain_props[NUM_TERRAINS];
-
+/*! \brief Colour and text properties for 'decals'
+ *
+ * The primary use of decals is to "decorate" the dungeon with gore as the
+ * player defeats enemies. This structure provides some naming information
+ * and the display colour.
+ */
struct Decal_desc
{
char const *name;
Gamecolour colour;
};
+/*! \brief Array of decal properties */
extern Decal_desc decal_props[NUM_DECALS];
-#define NO_STAIRS (-1)
-
#define TFLAG_opaque 0x00000001u
#define TFLAG_block_beings 0x00000002u
#define TFLAG_block_ether 0x00000004u
#define CHUNK_MASK 0xf
#define CHUNK_AREA (CHUNK_EDGE * CHUNK_EDGE)
-//! The fundamental object-like unit of cartography
+/*! \brief The fundamental object-like unit of cartography
+ *
+ * Every Level consists of one or more of these, which provide the actual
+ * data that the Level's accessor methods touch.
+ */
class Chunk
{
public:
#define MIN_ROOM_EDGE 4
+/*! \brief A lookup token for getting a Level
+ *
+ * As yet, not really that important; everything it supports could be done
+ * another way.
+ */
class Level_key
{
public:
bool operator <(Level_key right) const { return (dungeon < right.dungeon) || ((dungeon == right.dungeon) && (depth < right.depth)); }
};
+/*! \brief Global constant expressing the idea "not on a level" */
extern Level_key const No_level;
-/*! \brief Description of a staircase leading away from the level
- */
+/*! \brief Description of a staircase leading away from the level */
class Stair_detail
{
public:
Level_key destination;
};
+/*! \brief Global constant expressing the idea "invalid stair detail" */
extern Stair_detail const Bad_stairs;
-//! The top-tier object for describing everything about a level
+#define NO_STAIRS (-1)
+
+/*! \brief The top-tier object for describing everything about a level
+ *
+ * Generally speaking, this is the object that functions which are not map
+ * generators (and even functions which *are* map generators, most of the
+ * time!) should be using to get at the details of what does or does not
+ * exist and where it is.
+ */
class Level
{
public:
- Level_key self;
+ Level_key self; //!< The level knows where it is
Chunk ***chunks; //!< 16x16 subsections of the level, not necessarily dense
Offset origin_off; //!< Don't force a map size change to recalculate all Coords
+ uint32_t threat; //!< Threat rating of level, controlling generation
uint32_t chunks_high; //!< Chunkwise size of level in the y-direction
uint32_t chunks_wide; //!< Chunkwise size of level in the x-direction
Terrain dead_space; //!< Terrain to fill new chunks with and return for Coords in unpopulated chunks
std::deque<Stair_detail> stairs;
void *layout_data; //!< auxiliary data required by layout
void *theme_data; //!< auxiliary data required by layout
+ Mon_gen_table summons; //!< Used by nonspecific summoning effects
/* Member functions only past this point, please. */
Level() :
self(No_level), chunks(nullptr), origin_off(Stationary),
theme_data(nullptr)
{
}
+ void build(Level_key lk);
Terrain terrain_at(Coord c) const
{
if (in_bounds(c))
void vivify(Coord c);
};
+/*! \brief The global "current level" object
+ *
+ * This will be replaced with a pointer eventually (and functions that need
+ * to levels will be converted to take pointers, thus making the concept of
+ * simultaneously-live levels viable)
+ */
extern Level lvl;
-extern int depth;
+/*! \brief The global "current depth" value
+ *
+ * This will be going away. The player object will acquire a Level_key (and a
+ * Level *) member at the same time.
+ */
+extern uint32_t depth;
void leave_level(void);
void make_new_level(void);
bool terrain_blocks_beings(Terrain terr);
bool terrain_blocks_items(Terrain terr);
bool terrain_blocks_missiles(Terrain terr);
-void serialize_level(FILE *fp, Level const *l);
-void deserialize_level(FILE *fp, Level *l);
void level_cleanup(void);
+
#endif
/* map.hh */
#define GUIDE_EDGE_CHUNKS 3
#define GUIDE_EDGE_SIZE (GUIDE_EDGE_CHUNKS << CHUNK_SHIFT)
+/*! \brief Exception thrown when level generator suffers a serious failure */
+struct Game_levgen_excep
+{
+ char const *str;
+ Game_levgen_excep() = delete;
+ Game_levgen_excep(char const *s) : str(s) { }
+};
+
+/*! \brief Function pointer type for level-build functions */
+typedef void (*Levgen_build_func)(Level *l);
+
+/*! \brief Search-table entry for selecting a level layout */
+struct Layout_selector
+{
+ uint32_t threat;
+ int32_t tgt_num;
+ int32_t sides;
+ Level_layout layout;
+};
+
void build_level_shrine(Level *l);
void build_level_intrusions(Level *l);
void build_level_cave(Level *l);
-void build_level_pits(Level *l, Terrain t = CHASM);
+void build_level_pits(Level *l);
void build_level_dungeonbash(Level *l);
void build_level_claustrophobia(Level *l);
-Pass_fail get_levgen_mon_floor(Level *l, Coord *c);
+Success get_levgen_mon_floor(Level *l, Coord *c);
int excavation_write(Level *l, Coord c, void const *data);
int intrusion_write(Level *l, Coord c, void const *data);
void initialize_chunks(Level *l, int height, int width, bool dense);
Coord run_random_walk_unbounded(Level *l, Coord oc, rwalk_mod_funcptr func,
void const *priv_ptr, int cells);
-struct Game_levgen_excep
-{
- char const *str;
- Game_levgen_excep() = delete;
- Game_levgen_excep(char const *s) : str(s) { }
-};
+extern Montable_gen_func themed_montable_funcs[NUM_THEMES];
+
+extern Objtable_gen_func themed_objtable_funcs[NUM_THEMES];
#endif
(lvl.mon_at(testpos) == NO_MON) &&
(testpos != u.pos))
{
- pmon = get_random_pmon();
- if (pmon_is_magician(pmon))
+ do
{
- /* Never summon magicians! */
- continue;
- }
- mon = create_mon(NO_PMON, testpos);
+ pmon = lvl.summons.get_random_entry();
+ } while (pmon_is_magician(pmon));
+ mon = create_mon(pmon, testpos);
if (mon != NO_MON)
{
created++;
bool reject_mon(int pm)
{
- if ((permons[pm].power > depth) || (zero_die(100) < permons[pm].rarity))
+ if (permons[pm].genweight == 0)
+ {
+ return true;
+ }
+ if (permons[pm].power > depth)
{
return true;
}
return false;
}
-Pass_fail teleport_mon_to_you(Mon_handle mon)
+Success teleport_mon_to_you(Mon_handle mon)
{
int tryct;
Offset delta;
Coord c;
- int success = 0;
+ Success success = Success::fail;
Mon *mptr = mon_snapv(mon);
Coord oldpos = mptr->pos;
for (tryct = 0; tryct < 40; tryct++)
c = u.pos + delta;
if (mptr->can_pass(c))
{
- success = 1;
+ success = Success::pass;
break;
}
}
- if (success)
+ if (success == Success::pass)
{
reloc_mon(mon, c);
notify_mon_appears(mon);
- return You_pass;
}
- return You_fail;
+ return success;
}
-Pass_fail teleport_mon(Mon_handle mon)
+Success teleport_mon(Mon_handle mon)
{
- Pass_fail rval = You_fail;
+ Success rval = Success::fail;
int cell_try;
Coord c;
for (cell_try = 0; cell_try < 200; cell_try++)
if ((lvl.mon_at(c) == NO_MON) && (lvl.terrain_at(c) == FLOOR) && (c != u.pos))
{
reloc_mon(mon, c);
- rval = You_pass;
+ rval = Success::pass;
break;
}
}
{
if (!zero_die(4))
{
- create_obj_class_near(POCLASS_SCROLL, 1, false, c);
+ create_obj_near(random_pobj_by_class(POCLASS_SCROLL), 1, c);
}
else if (!zero_die(3))
{
- create_obj_class_near(POCLASS_POTION, 1, false, c);
+ create_obj_near(random_pobj_by_class(POCLASS_SCROLL), 1, c);
}
}
extern Death_drop_entry death_drops[];
+struct Mon_row
+{
+ int32_t pm;
+ uint32_t weight;
+};
+
+struct Mon_gen_table
+{
+ std::vector<Mon_row> vec;
+ uint32_t total_weight;
+ Mon_gen_table() : vec(), total_weight(0) { }
+ int32_t get_random_entry(void);
+};
+
+typedef void (*Montable_gen_func)(Mon_gen_table *table);
+
+
#define PLAYER_MON (-2)
void update_mon(Mon_handle mon);
int knockback_mon(Mon_handle mon, Offset step, bool cansee, bool by_you);
void move_mon(Mon_handle mon, Coord c);
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. */
+Success teleport_mon(Mon_handle mon); /* Randomly relocate monster. */
+Success 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, bool explode_corpse = false);
void unplace_mon(Mon_handle mon);
#define NOTIFY_LOCAL_TTY_CC
#include "victrix-abyssi.hh"
-#include "monsters.hh"
-#include "objects.hh"
-#include "player.hh"
-#include "map.hh"
+#include "notify.hh"
+#include "display.hh"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
void notify_learned_skill(Skill_id skill)
{
print_msg("You learned a new skill: %s\n", skill_props[skill].name);
- print_msg("%s\n", skill_props[skill].desc);
+ print_msg("\n%s\n\n", skill_props[skill].desc);
}
void notify_leadfoot_recovered(void)
{
char *s;
asprint_mon_name(&s, mon, 2);
- print_msg("%s dies.\n", &s);
+ print_msg("%s dies.\n", s);
free(s);
}
}
asprint_mon_name(&s, mon, 2);
if (tag != NO_DECAL)
{
- print_msg("%s explodes in a welter of %s!\n", s, decal_props[tag]);
+ print_msg("%s explodes in a welter of %s!\n", s, decal_props[tag].name);
}
else
{
switch (po)
{
-#if 0
case PO_WINDSWORD:
print_msg("Your stride lightens as you ready the magic blade.\n");
break;
-#endif
case PO_HELLGLAIVE:
print_msg("Infernal power suffuses you as you heft the cruel pole-arm.\n");
break;
void notify_get_item(Obj_handle from, int slot)
{
+ char *inv;
+ char *floor;
+ asprint_obj_name(&inv, u.inventory[slot]);
if (from != NO_OBJ)
{
- print_msg("You get ");
- print_obj_name(from);
- print_msg(".\nYou now have\n");
- print_msg("%c) ", 'a' + slot);
- print_obj_name(u.inventory[slot]);
- print_msg("\n");
+ asprint_obj_name(&floor, from);
+ print_msg("You get %s.\nYou now have\n%c) %s.\n",
+ floor, 'a' + slot, inv);
+ free(floor);
}
else
{
- print_msg("You now have\n");
- print_msg("%c) ", 'a' + slot);
- print_obj_name(u.inventory[slot]);
- print_msg("\n");
+ print_msg("You now have\n%c) %s.\n", 'a' + slot, inv);
}
+ free(inv);
}
void notify_pack_full(void)
void debug_unimplemented_activation(int po)
{
- print_msg(Msg_prio::Bug, "BUG: permobj %d should be activatable but has no activation handler.\n");
+ print_msg(Msg_prio::Bug, "BUG: permobj %d should be activatable but has no activation handler.\n", po);
}
void debug_unimplemented_break_reaction(int po)
{
- print_msg(Msg_prio::Bug, "BUG: permobj %d should react to hitting durability 0 but has no break reaction handler.\n");
+ print_msg(Msg_prio::Bug, "BUG: permobj %d should react to hitting durability 0 but has no break reaction handler.\n", po);
}
void debug_mon_invalid_move(Mon_handle mon, Coord c)
#include <string.h>
+/* globals */
std::map<Obj_handle, Obj> objects;
const Obj_handle NO_OBJ = 0u;
-int get_random_pobj(void);
-
/*! \brief Read a magic scroll */
Action_cost read_scroll(Obj_handle obj)
{
return first_free_obj_handle++;
}
-Obj_handle create_obj_class_near(enum poclass_num po_class, int quantity, bool with_you, Coord c)
-{
- int po_idx;
- int tryct;
- for (tryct = 0; tryct < 200; tryct++)
- {
- switch (po_class)
- {
- case POCLASS_POTION:
- po_idx = inc_flat(PO_FIRST_POTION, PO_LAST_POTION);
- break;
- case POCLASS_SCROLL:
- po_idx = inc_flat(PO_FIRST_SCROLL, PO_LAST_SCROLL);
- break;
- case POCLASS_RING:
- po_idx = inc_flat(PO_FIRST_RING, PO_LAST_RING);
- break;
- default:
- /* No getting armour/weapons by class... yet. */
- return NO_OBJ;
- }
- if (zero_die(100) < permobjs[po_idx].rarity)
- {
- continue;
- }
- break;
- }
- return (with_you ? create_obj(po_idx, quantity, true, c) : create_obj_near(po_idx, quantity, c));
-}
-
-int get_random_pobj(void)
-{
- int tryct;
- int po_idx;
- for (tryct = 0; tryct < 200; tryct++)
- {
- po_idx = zero_die(NUM_OF_PERMOBJS);
- if (zero_die(100) < permobjs[po_idx].rarity)
- {
- po_idx = NO_POBJ;
- continue;
- }
- if (depth < permobjs[po_idx].depth)
- {
- po_idx = NO_POBJ;
- continue;
- }
- break;
- }
- 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;
debug_object_pool_exhausted();
return NO_OBJ;
}
- if (po_idx == NO_POBJ)
+ if ((po_idx == NO_POBJ) || (po_idx >= NUM_OF_PERMOBJS))
{
- po_idx = get_random_pobj();
- if (po_idx == NO_POBJ)
- {
- debug_pobj_select_failed();
- return NO_OBJ;
- }
+ DEBUG_ARBITRARY("create_obj called with invalid po_idx\n");
+ return NO_OBJ;
}
Obj o;
memset(o.meta, '\0', sizeof o.meta);
}
}
-Damtyp po_resistance(int po)
-{
- if ((po < 0) || (po >= NUM_OF_PERMOBJS))
- {
- return DT_NONE;
- }
- uint32_t res = permobjs[po].flags[1] & POF_RES_MASK;
- // Items never grant physical damage resistance
- if ((res > 0) && (res <= LAST_DAMTYPE))
- {
- return Damtyp(res);
- }
- return DT_NONE;
-}
-
-Damtyp po_damage_amp(int po)
-{
- if ((po < 0) || (po >= NUM_OF_PERMOBJS))
- {
- return DT_NONE;
- }
- uint32_t dt = (permobjs[po].flags[1] & POF_DMG_MASK) >> POF_DMG_SHIFT;
- // Items never grant physical damage amplification
- if ((dt > 0) && (dt <= LAST_DAMTYPE))
- {
- return Damtyp(dt);
- }
- return DT_NONE;
-}
-
-bool po_is_sword(int po)
-{
- return (permobjs[po].flags[0] & POF_SWORD);
-}
-
-bool po_is_dagger(int po)
-{
- return (permobjs[po].flags[0] & POF_DAGGER);
-}
-
-bool po_is_polearm(int po)
-{
- return (permobjs[po].flags[0] & POF_POLEARM);
-}
-
-bool po_is_bludgeon(int po)
-{
- return (permobjs[po].flags[0] & POF_BLUDGEON);
-}
-
-bool po_is_demonic(int po)
-{
- return (permobjs[po].flags[0] & POF_DEMONIC);
-}
-
-bool po_is_dress(int po)
-{
- return (permobjs[po].flags[0] & POF_DRESS);
-}
-
-bool po_is_leatherwear(int po)
-{
- return (permobjs[po].flags[0] & POF_LEATHERY);
-}
-
-bool po_is_robe(int po)
-{
- return (permobjs[po].flags[0] & POF_ROBE);
-}
-
-bool po_is_stackable(int po)
-{
- return (permobjs[po].flags[0] & POF_STACKABLE);
-}
-
-bool po_grants_speed(int po)
-{
- return (permobjs[po].flags[1] & POF_SPEED);
-}
-
-bool po_grants_flight(int po)
-{
- return (permobjs[po].flags[1] & POF_FLIGHT);
-}
-
-bool po_grants_protective(int po)
-{
- return (permobjs[po].flags[1] & POF_PROTECTIVE);
-}
-
-bool po_grants_passwater(int po)
-{
- return (permobjs[po].flags[1] & POF_PASS_WATER);
-}
-
void attempt_pickup(void)
{
int i;
return Cost_std;
}
-Pass_fail role_equip_check(Obj_handle obj, Noisiness noisy)
+Success role_equip_check(Obj_handle obj, Noisiness noisy)
{
Obj const *optr = obj_snap(obj);
if ((u.role == Role_demon_hunter) && po_is_demonic(optr->po_ref))
{
notify_role_blocks_equip();
- return You_fail;
+ return Success::fail;
}
- return You_pass;
+ return Success::pass;
}
Action_cost player_wield(Obj_handle obj, Noisiness noisy)
{
- Pass_fail rej = role_equip_check(obj, noisy);
- if (rej == You_pass)
+ Success rej = role_equip_check(obj, noisy);
+ if (rej == Success::pass)
{
if (u.weapon != NO_OBJ)
{
return Cost_std;
}
-Pass_fail unequip_safety_check(uint32_t mask, Noisiness noisy)
+Success unequip_safety_check(uint32_t mask, Noisiness noisy)
{
Terrain t = lvl.terrain_at(u.pos);
if (terrain_is_hot(t) &&
{
notify_hot_blocks_unequip();
}
- return You_fail;
+ return Success::fail;
}
else if (terrain_drowns(t) &&
(!(u.passwater & ~mask)) &&
{
notify_wet_blocks_unequip();
}
- return You_fail;
+ return Success::fail;
}
else if (terrain_gapes(t) &&
(!(u.flight & ~mask)))
{
notify_pit_blocks_unequip();
}
- return You_fail;
+ return Success::fail;
}
- return You_pass;
+ return Success::pass;
}
bool corpse_is_blessed(Obj const *optr)
{
- return (optr && (optr->po_ref == PO_CORPSE) && (optr->meta[1] == 1));
+ return (optr && po_is_corpse(optr->po_ref) && (optr->meta[1] == 1));
}
/* objects.cc */
#define OBJ2_CC
#include "victrix-abyssi.hh"
-#include "objects.hh"
-#include "monsters.hh"
+#include "pobj_id.hh"
#include "fov.hh"
#include <string.h>
{ NO_POBJ, nullptr, nullptr, nullptr },
};
+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;
+}
+
/* obj2.cc */
// vim:cindent
extern Weapon_table_entry weapon_table[];
+struct Armour_table_entry
+{
+ int pobj;
+ void (*on_equip)(Obj_handle obj);
+ void (*on_unequip)(Obj_handle obj);
+ void (*on_magic)(Obj_handle obj);
+};
+
+extern Armour_table_entry armour_table[];
+
+struct Ring_table_entry
+{
+ int pobj;
+ void (*on_equip)(Obj_handle obj);
+ void (*on_unequip)(Obj_handle obj);
+ void (*on_magic)(Obj_handle obj);
+};
+
+extern Ring_table_entry ring_table[];
+
+struct Obj_row
+{
+ int32_t po;
+ uint32_t weight;
+};
+
+struct Obj_gen_table
+{
+ std::vector<Obj_row> vec;
+ int32_t total_weight;
+ Obj_gen_table() : vec(), total_weight(0) { }
+ int32_t get_random_entry(void);
+};
+
+typedef void (*Objtable_gen_func)(Obj_gen_table *table);
+
+
/* 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);
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_near(enum poclass_num pocl, int quantity, bool with_you, Coord c);
void damage_obj(Obj_handle obj);
int evasion_penalty(Obj_handle obj);
bool corpse_is_blessed(Obj const *optr);
Action_cost player_unwield(Noisiness noisy = Noise_std);
Action_cost player_wield(Obj_handle obj, Noisiness noisy = Noise_std);
void attempt_pickup(void);
-Pass_fail unequip_safety_check(uint32_t mask, Noisiness noisy = Noise_std);
+Success unequip_safety_check(uint32_t mask, Noisiness noisy = Noise_std);
#endif
#include "pobj_id.hh"
-/*! \brief Quality level of object.
- */
+/*! \brief Quality level of object. */
enum Item_quality
{
Quality_bad, //!< Useless or hypothetically actively harmful
Quality_legendary
};
-/* XXX enum poclass_num */
-/* Categories of permanent object. */
-enum poclass_num {
+/*! \brief Categories of permanent object */
+enum Poclass_num {
POCLASS_NONE = 0, POCLASS_WEAPON, POCLASS_POTION,
POCLASS_SCROLL, POCLASS_FLASK, POCLASS_ARMOUR, POCLASS_RING,
POCLASS_FOOD, POCLASS_CARRION
};
-#include "pobj_id.hh"
-
#define POBJ_FLAG_WORDS 2
// POF field 0
#define POF_BLUDGEON 0x00100000u
#define POF_DAGGER 0x00200000u
#define POF_WHIP 0x00400000u
+#define POF_BOW 0x00800000u
+#define POF_CROSSBOW 0x01000000u
#define POF_DEMONIC 0x10000000u
// POF field 1
#define POF_RES_FIRE ((uint32_t) DT_FIRE)
#define POF_PASS_WATER 0x00010000u
#define POF_FLIGHT 0x00020000u
#define POF_PROTECTIVE 0x00100000u // all damage from enemy attacks halved
+#define POF_REGEN 0x00200000u // faster passive healing
#define POF_SPEED 0x01000000u // bumps you up one speed band
/*! \brief The 'permanent object' database */
char const *name; //!< English-language name of item
char const *plural; //!< English-language plural of item
char const *description; //!< English-language description of item
- enum poclass_num poclass; //!< Category of item
- int rarity; //!< Chance in 100 of being thrown away and regen'd.
+ Poclass_num poclass; //!< Category of item
+ uint32_t genweight; //!< Chance in 100 of being thrown away and regen'd.
char sym; //!< must be in range 32...126
char const *unicode; //!< must be a C-style string in UTF-8 encoding
Gamecolour colour; //!< colour used for terminal display
int power; //!< first POCLASS-specific data field
int power2; //!< second POCLASS-specific data field
- int depth; //!< shallowest depth at which item can be randomly gen'd
+ uint32_t depth; //!< shallowest depth at which item can be randomly gen'd
uint32_t flags[POBJ_FLAG_WORDS];
};
#define NO_POBJ (-1)
extern const int NUM_OF_PERMOBJS;
extern Permobj permobjs[];
+typedef Success(*Pobj_filter)(int po);
+
bool po_is_stackable(int po);
bool po_is_dress(int po);
bool po_is_leatherwear(int po);
bool po_is_dagger(int po);
bool po_is_polearm(int po);
bool po_is_bludgeon(int po);
+bool po_is_bow(int po);
+bool po_is_crossbow(int po);
bool po_is_demonic(int po);
+bool po_is_corpse(int po);
Damtyp po_resistance(int po);
Damtyp po_damage_amp(int po);
bool po_grants_flight(int po);
bool po_grants_passwater(int po);
bool po_grants_protective(int po);
bool po_grants_speed(int po);
+int random_pobj_by_class(Poclass_num po_class);
#endif
char sym;
char const *unicode;
int colour;
- int rarity; /* Chance in 100 of being thrown back and regen'd. */
- int power; /* Used to determine OOD rating. */
+ uint32_t genweight; /*!< Weighting in standard-algorithm selection */
+ uint32_t power; /*!< Used to determine OOD rating. */
/* All OOD-improved stats cap out at base + (power * base) */
- int hp; /* Improved by OOD rating at 1:1. */
- int mtohit; /* Improved by OOD rating at 1:3. */
- int rtohit; /* Improved by OOD rating at 1:3. */
- int mdam; /* Improved by OOD rating at 1:5. */
- int rdam; /* Improved by OOD rating at 1:5. */
- Damtyp rdtyp; /* type of damage used by ranged attack. */
- char const *shootverb; /* shooting verb e.g. "fires an arrow", "breathes". */
- int defence; /* Improved by OOD rating at 1:3. */
- int exp; /* Unaffected by OOD rating. */
- int speed; /* 0 = slow; 1 = normal; 2 = quick */
- uint32_t flags[PERMON_FLAG_FIELDS]; /* resistances, AI settings, etc. */
+ int hp; /*!< Improved by OOD rating at 1:1. */
+ int mtohit; /*!< Improved by OOD rating at 1:3. */
+ int rtohit; /*!< Improved by OOD rating at 1:3. */
+ int mdam; /*!< Improved by OOD rating at 1:5. */
+ int rdam; /*!< Improved by OOD rating at 1:5. */
+ Damtyp rdtyp; /*!< type of damage used by ranged attack. */
+ char const *shootverb; /*!< shooting verb e.g. "fires an arrow", "breathes". */
+ int defence; /*!< Improved by OOD rating at 1:3. */
+ int exp; /*!< Unaffected by OOD rating. */
+ int speed; /*!< 0 = slow; 1 = normal; 2 = quick */
+ uint32_t flags[PERMON_FLAG_FIELDS]; /*!< resistances, AI settings, etc. */
};
extern struct Permon permons[];
extern int const NUM_OF_PERMONS;
#include "core.hh"
#endif
-enum Skill_id
-{
- NO_SKILL = -1,
- Skill_power_attack = 0,
- /* Princess skills */
- Skill_blood_royal,
- Skill_flying_leap,
- Skill_regal_radiance,
- /* Demon Hunter skills */
- Skill_charge,
- Skill_blade_dance,
- Skill_demon_slayer,
- /* Thanatophile skills */
- Skill_deaths_grace,
- Skill_deaths_vengeance,
- Skill_sanctified_by_death
-};
-
-#define LAST_SKILL (Skill_sanctified_by_death)
-#define NUM_SKILLS (1 + LAST_SKILL)
-
/*! \brief Skill descriptor */
struct Skill_desc
{
extern Skill_gain_table universal_skills[];
extern Skill_gain_table *skill_tables[NUM_ROLES];
+typedef void (*Skill_update_func)(void);
+extern Skill_update_func skill_update_funcs[NUM_SKILLS];
+
/*! \brief Internal representation of the player character
*/
#define INVENTORY_SIZE 19
uint32_t passwater; //!< Can currently walk on water tiles
uint32_t flight; //!< Ignore all hazardfloors and pits
uint32_t protective_gear; //!< Reduce all damage taken
+ uint32_t regen; //!< Faster passive healing
int level; //!< Current experience level.
Obj_handle inventory[INVENTORY_SIZE]; //!< currently carried items.
Obj_handle weapon; //!< currently equipped weapon.
Action_cost move_player(Offset delta);
void reloc_player(Coord c);
void recalc_defence(void);
-Pass_fail teleport_u(void);
+Success teleport_u(void);
void update_player(void);
inline bool empty_handed(void) { return u.weapon == NO_OBJ; }
bool wielding_melee_weapon(void);
extern Combo_entry combo_entries[];
-/*! \brief ID numbers for skills */
#endif
/* player.hh */
#! /usr/bin/env perl
+#
+# pmon_comp - monster preprocessor for a family of roguelikes
+#
+# Copyright 2014 Martin Read
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# 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.
+
use strict;
use warnings;
'ascii' => $href->{ascii},
'uni' => $href->{uni},
'colour' => $href->{colour},
- 'rarity' => $href->{rarity},
+ 'genweight' => $href->{genweight},
'power' => $href->{power},
'hp' => $href->{hp},
'mtohit' => $href->{mtohit},
our %blank_monster = (
'desc' => "An monster some useless slacker hasn't bothered to describe.",
'colour' => "l_grey",
- 'rarity' => 100,
+ 'genweight' => 0,
'power' => 1,
'hp' => 1,
'mtohit' => 0,
die("Negative or non-numeric rdam value $pm in monster $working_monster{name}");
}
}
- elsif ($input_line =~ /^\s*rarity\s+/i)
+ elsif ($input_line =~ /^\s*genweight\s+/i)
{
my $pm = "$POSTMATCH";
if ($pm =~ /^([0-9]+)/)
{
- $working_monster{rarity} = $1;
+ $working_monster{genweight} = $1;
}
else
{
- die("Negative or non-numeric rarity value $pm in monster $working_monster{name}");
+ die("Negative or non-numeric genweight value $pm in monster $working_monster{name}");
}
}
elsif ($input_line =~ /^\s*shootverb\s+/i)
print HEADERFILE ",\n";
}
print HEADERFILE " ${tagname}";
- printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", %s, %s, Gcol_%s, %d, %d, %d, %d, %d, %d, %d, DT_%s, \"%s\", %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{rarity}, $phref->{power}, $phref->{hp}, $phref->{mtohit}, $phref->{rtohit}, $phref->{mdam}, $phref->{rdam}, $phref->{rdtyp}, $phref->{shootverb}, $phref->{defence}, $phref->{exp}, $phref->{speed}, flag_string($phref->{flags});
+ printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", %s, %s, Gcol_%s, %d, %d, %d, %d, %d, %d, %d, DT_%s, \"%s\", %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{genweight}, $phref->{power}, $phref->{hp}, $phref->{mtohit}, $phref->{rtohit}, $phref->{mdam}, $phref->{rdam}, $phref->{rdtyp}, $phref->{shootverb}, $phref->{defence}, $phref->{exp}, $phref->{speed}, flag_string($phref->{flags});
}
printf SOURCEFILE "};\nconst int NUM_OF_PERMONS = %d;\n// permons.cc\n", $total_mons;
--- /dev/null
+/* \file pobj2.cc
+ * \brief permobj-related functions
+ */
+
+/* Copyright © 2014 Martin Read
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 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.
+ */
+
+#define POBJ2_CC
+#include "core.hh"
+#include "permobj.hh"
+#include "pobj_id.hh"
+#include "notify.hh"
+
+int random_pobj_by_class(Poclass_num po_class)
+{
+ switch (po_class)
+ {
+ case POCLASS_POTION:
+ return inc_flat(PO_FIRST_POTION, PO_LAST_POTION);
+ case POCLASS_SCROLL:
+ return inc_flat(PO_FIRST_SCROLL, PO_LAST_SCROLL);
+ case POCLASS_RING:
+ return inc_flat(PO_FIRST_RING, PO_LAST_RING);
+ case POCLASS_WEAPON:
+ return inc_flat(PO_FIRST_WEAPON, PO_LAST_WEAPON);
+ case POCLASS_ARMOUR:
+ return inc_flat(PO_FIRST_ARMOUR, PO_LAST_ARMOUR);
+ case POCLASS_FOOD:
+ return inc_flat(PO_FIRST_FOOD, PO_LAST_FOOD);
+ case POCLASS_CARRION:
+ return PO_CORPSE; // but man oh man we need to work on this.
+ default:
+ DEBUG_ARBITRARY("Unsupported po_class requested");
+ return NO_POBJ;
+ }
+
+}
+
+int get_random_pobj(Poclass_num po_class, Pobj_filter filter)
+{
+ int tryct;
+ int po_idx;
+ for (tryct = 0; (tryct < 200) && (po_idx != NO_POBJ); tryct++)
+ {
+ if (po_class != POCLASS_NONE)
+ {
+ po_idx = random_pobj_by_class(po_class);
+ }
+ else
+ {
+ po_idx = zero_die(NUM_OF_PERMOBJS);
+ }
+ if (filter && (filter(po_idx) == Success::fail))
+ {
+ po_idx = NO_POBJ;
+ }
+ }
+ return po_idx;
+}
+
+Damtyp po_resistance(int po)
+{
+ if ((po < 0) || (po >= NUM_OF_PERMOBJS))
+ {
+ return DT_NONE;
+ }
+ uint32_t res = permobjs[po].flags[1] & POF_RES_MASK;
+ // Items never grant physical damage resistance
+ if ((res > 0) && (res <= LAST_DAMTYPE))
+ {
+ return Damtyp(res);
+ }
+ return DT_NONE;
+}
+
+Damtyp po_damage_amp(int po)
+{
+ if ((po < 0) || (po >= NUM_OF_PERMOBJS))
+ {
+ return DT_NONE;
+ }
+ uint32_t dt = (permobjs[po].flags[1] & POF_DMG_MASK) >> POF_DMG_SHIFT;
+ // Items never grant physical damage amplification
+ if ((dt > 0) && (dt <= LAST_DAMTYPE))
+ {
+ return Damtyp(dt);
+ }
+ return DT_NONE;
+}
+
+bool po_is_sword(int po)
+{
+ return (permobjs[po].flags[0] & POF_SWORD);
+}
+
+bool po_is_dagger(int po)
+{
+ return (permobjs[po].flags[0] & POF_DAGGER);
+}
+
+bool po_is_polearm(int po)
+{
+ return (permobjs[po].flags[0] & POF_POLEARM);
+}
+
+bool po_is_bludgeon(int po)
+{
+ return (permobjs[po].flags[0] & POF_BLUDGEON);
+}
+
+bool po_is_demonic(int po)
+{
+ return (permobjs[po].flags[0] & POF_DEMONIC);
+}
+
+bool po_is_bow(int po)
+{
+ return (permobjs[po].flags[0] & POF_BOW);
+}
+
+bool po_is_crossbow(int po)
+{
+ return (permobjs[po].flags[0] & POF_CROSSBOW);
+}
+
+bool po_is_dress(int po)
+{
+ return (permobjs[po].flags[0] & POF_DRESS);
+}
+
+bool po_is_leatherwear(int po)
+{
+ return (permobjs[po].flags[0] & POF_LEATHERY);
+}
+
+bool po_is_robe(int po)
+{
+ return (permobjs[po].flags[0] & POF_ROBE);
+}
+
+bool po_is_stackable(int po)
+{
+ return (permobjs[po].flags[0] & POF_STACKABLE);
+}
+
+bool po_grants_speed(int po)
+{
+ return (permobjs[po].flags[1] & POF_SPEED);
+}
+
+bool po_grants_flight(int po)
+{
+ return (permobjs[po].flags[1] & POF_FLIGHT);
+}
+
+bool po_grants_protective(int po)
+{
+ return (permobjs[po].flags[1] & POF_PROTECTIVE);
+}
+
+bool po_grants_passwater(int po)
+{
+ return (permobjs[po].flags[1] & POF_PASS_WATER);
+}
+
+int evasion_penalty(int po)
+{
+ if (permobjs[po].poclass == POCLASS_ARMOUR)
+ {
+ return permobjs[po].power2;
+ }
+ return 100;
+}
+
+bool po_is_corpse(int po)
+{
+ return (po == PO_CORPSE);
+}
+
+/* objects.cc */
+// vim:cindent:expandtab
#! /usr/bin/env perl
+#
+# pobj_comp - object preprocessor for a family of roguelikes
+#
+# Copyright 2014 Martin Read
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# 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.
use strict;
use warnings;
'PASS_WATER' => 1,
'FLIGHT' => 1,
'PROTECTIVE' => 1,
- 'SPEED' => 1
+ 'REGEN' => 1,
+ 'SPEED' => 1,
);
'ascii' => $href->{ascii},
'uni' => $href->{uni},
'colour' => $href->{colour},
- 'rarity' => $href->{rarity},
+ 'genweight' => $href->{genweight},
'power' => $href->{power},
'power2' => $href->{power2},
'depth' => $href->{depth},
close INFILE;
our %blank_object = (
'desc' => "An object some useless slacker hasn't bothered to describe.",
- 'rarity' => 100,
+ 'genweight' => 0,
'power' => 0,
'power2' => 0,
'depth' => 1
die("Negative or non-numeric depth value $pm in object $working_object{name}");
}
}
- elsif ($input_line =~ /^\s*RARITY\s+/)
+ elsif ($input_line =~ /^\s*GENWEIGHT\s+/)
{
my $pm = "$POSTMATCH";
if ($pm =~ /^([0-9]+)/)
{
- $working_object{rarity} = $1;
+ $working_object{genweight} = $1;
}
else
{
- die("Negative or non-numeric rarity value $pm in object $working_object{name}");
+ die("Negative or non-numeric genweight value $pm in object $working_object{name}");
}
}
elsif ($input_line =~ /^\s*COLOUR\s+/)
print HEADERFILE ",\n";
}
print HEADERFILE " ${tagname}";
- printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_WEAPON, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{rarity}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
+ printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_WEAPON, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{genweight}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
}
if (defined($tagname))
{
print HEADERFILE ",\n";
}
print HEADERFILE " ${tagname}";
- printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_ARMOUR, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{rarity}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
+ printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_ARMOUR, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{genweight}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
}
if (defined($tagname))
{
print HEADERFILE ",\n";
}
print HEADERFILE " ${tagname}";
- printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_FOOD, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{rarity}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
+ printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_FOOD, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{genweight}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
}
if (defined($tagname))
{
print HEADERFILE ",\n";
}
print HEADERFILE " ${tagname}";
- printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_SCROLL, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{rarity}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
+ printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_SCROLL, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{genweight}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
}
if (defined($tagname))
{
print HEADERFILE ",\n";
}
print HEADERFILE " ${tagname}";
- printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_POTION, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{rarity}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
+ printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_POTION, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{genweight}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
}
if (defined($tagname))
{
print HEADERFILE ",\n";
}
print HEADERFILE " ${tagname}";
- printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_RING, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{rarity}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
+ printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_RING, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{genweight}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
}
if (defined($tagname))
{
print HEADERFILE ",\n";
}
print HEADERFILE " ${tagname}";
- printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_CARRION, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{rarity}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
+ printf SOURCEFILE " { \"%s\", \"%s\", \"%s\", POCLASS_CARRION, %d, %s, %s, Gcol_%s, %d, %d, %d, { %s } },\n", $phref->{name}, $phref->{plural}, $phref->{desc}, $phref->{genweight}, $phref->{ascii}, $phref->{uni}, $phref->{colour}, $phref->{power}, $phref->{power2}, $phref->{depth}, flag_string($phref->{flags});
}
if (defined($tagname))
{
thanatophile_skills
};
+void sanctified_by_death_update(void)
+{
+ for (int i = 0; i < INVENTORY_SIZE; ++i)
+ {
+ Obj *optr = obj_snapv(u.inventory[i]);
+ if (optr && (optr->po_ref == PO_CORPSE))
+ {
+ optr->meta[1] = 1; // bless this corpse
+ }
+ }
+ Coord c;
+ for (c.y = u.pos.y - 1; c.y <= u.pos.x + 1; ++c.y)
+ {
+ for (c.x = u.pos.x - 1; c.x <= u.pos.x + 1; ++c.x)
+ {
+ Obj_handle o = lvl.obj_at(c);
+ Obj *optr = obj_snapv(o);
+ if (optr && (optr->po_ref == PO_CORPSE))
+ {
+ optr->meta[1] = 1;
+ }
+ }
+ }
+}
+
+Skill_update_func skill_update_funcs[NUM_SKILLS] =
+{
+ nullptr, // power_attack_update
+ nullptr, // blood_royal_update
+ nullptr, // flying_leap_update
+ nullptr, // regal_radiance_update
+ nullptr, // chage_update
+ nullptr, // blade_dance_update
+ nullptr, // demon_slayer_update
+ nullptr, // deaths_grace_update
+ nullptr, // deaths_vengeance_update
+ sanctified_by_death_update
+};
+
/* skills.cc */
// vim:cindent:expandtab
u.passwater &= BONUS_MASK_TEMPORARY;
u.flight &= BONUS_MASK_TEMPORARY;
u.protective_gear &= BONUS_MASK_TEMPORARY;
+ u.regen &= BONUS_MASK_TEMPORARY;
u.speed = (u.leadfoot ? 0 : 1);
u.defence = u.withering ? (u.agility / 10) : (u.agility / 5);
if (u.armour != NO_OBJ)
if (u.weapon != NO_OBJ)
{
Obj const *wep = obj_snap(u.weapon);
- if ((wep->po_ref == PO_BOW) ||
- (wep->po_ref == PO_CROSSBOW) ||
- (wep->po_ref == PO_THUNDERBOW))
+ if (po_is_bow(wep->po_ref) || po_is_crossbow(wep->po_ref))
{
notify_swing_bow();
return Cost_none;
}
}
-Pass_fail teleport_u(void)
+Success teleport_u(void)
{
int cell_try;
Coord c;
{
reloc_player(c);
notify_player_teleport();
- return You_pass;
+ return Success::pass;
}
}
notify_player_telefail();
- return You_fail;
+ return Success::fail;
}
void update_player(void)
{
- Obj const *ring = obj_snap(u.ring);
- //Obj const *weapon = obj_snap(u.weapon);
- //Obj const *armour = obj_snap(u.armour);
if (!(game_tick % 5) && (u.food >= 0) && (u.hpcur < u.hpmax))
{
/* Heal player for one hit point; do not allow HP gain,
/* Once you hit the nutrition endstop, your ring of regeneration stops
* working, and like normal regen, it won't raise you above 75% HP if
* your food counter is negative. */
- if (((game_tick % 10) == 5) && (ring) &&
- (ring->po_ref == PO_REGENERATION_RING) &&
+ if (u.regen && ((game_tick % 10) == 5) &&
(u.hpcur < ((u.food >= 0) ? u.hpmax : ((u.hpmax * 3) / 4))) &&
(u.food > MIN_FOOD))
{
{
int food_use = 1;
int squeal = 0;
- if (ring && (ring->po_ref == PO_REGENERATION_RING) &&
- !(game_tick % 2) && (u.food > MIN_FOOD))
+ if (u.regen && !(game_tick % 2) && (u.food > MIN_FOOD))
{
/* If you are still less hungry than MIN_FOOD nutrition,
* use one more food every second tick if you are
u.food -= food_use;
notify_food_use(food_use, squeal);
}
- if (u.known_skill[Skill_sanctified_by_death])
+ for (int sk = 0; sk < NUM_SKILLS; ++sk)
{
- for (int i = 0; i < INVENTORY_SIZE; ++i)
+ if (u.known_skill[sk] && (skill_update_funcs[sk] != nullptr))
{
- Obj *optr = obj_snapv(u.inventory[i]);
- if (optr && (optr->po_ref == PO_CORPSE))
- {
- optr->meta[1] = 1; // bless this corpse
- }
- }
- Coord c;
- for (c.y = u.pos.y - 1; c.y <= u.pos.x + 1; ++c.y)
- {
- for (c.x = u.pos.x - 1; c.x <= u.pos.x + 1; ++c.x)
- {
- Obj_handle o = lvl.obj_at(c);
- Obj *optr = obj_snapv(o);
- if (optr && (optr->po_ref == PO_CORPSE))
- {
- optr->meta[1] = 1;
- }
- }
+ skill_update_funcs[sk]();
}
}
if (u.leadfoot > 0)
/* change WIZARD_MODE to 1 if you want the wizard mode commands. */
#define WIZARD_MODE 0
-#ifndef PLAYER_HH
-#include "player.hh"
+#ifndef OBJECTS_HH
+#include "objects.hh"
#endif
#ifndef MONSTERS_HH
#include "monsters.hh"
#endif
-#ifndef DISPLAY_HH
-#include "display.hh"
-#endif
-
#ifndef MAP_HH
#include "map.hh"
#endif
#include "fov.hh"
#endif
+#ifndef PLAYER_HH
+#include "player.hh"
+#endif
+
void new_game(char const *name, Role_id role);
void main_loop(void);