cave.cc
combat.cc
combat.hh
+combo.cc
coord.cc
coord.hh
core.hh
+deeds.cc
default.permobjs
default.permons
display.hh
display-nc.cc
+dungeon.cc
fov.cc
fov.hh
log.cc
map.cc
map.hh
mapgen.hh
+mon1.cc
mon2.cc
-monsters.cc
+mon3.cc
monsters.hh
notes.txt
notify.hh
notify-local-tty.cc
-objects.cc
+obj1.cc
+obj2.cc
objects.hh
permobj.hh
permon.hh
pobj_comp
rng.cc
rng.hh
+role.cc
shrine.cc
+skills.cc
sorcery.cc
sorcery.hh
u.cc
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 monsters.o mon2.o mon3.o notify-local-tty.o objects.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 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
OBJS:=$(GENERATED_OBJS) $(HANDWRITTEN_OBJS)
GAME:=victrix-abyssi
MAJVERS:=0
MINVERS:=9
-PATCHVERS:=1
-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)
+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)
PRODUCTION_CFLAGS:=$(COMMON_CFLAGS) -O2
DEVELOPMENT_CFLAGS:=$(COMMON_CFLAGS) -g -Werror
map.o: $(srcdir)/map.cc $(srcdir)/$(GAME).hh $(srcdir)/map.hh $(srcdir)/core.hh $(srcdir)/notify.hh $(srcdir)/objects.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
-monsters.o: $(srcdir)/monsters.cc $(srcdir)/$(GAME).hh $(srcdir)/monsters.hh $(srcdir)/notify.hh $(srcdir)/objects.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
+mon1.o: $(srcdir)/mon1.cc $(srcdir)/$(GAME).hh $(srcdir)/monsters.hh $(srcdir)/notify.hh $(srcdir)/objects.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
mon2.o: $(srcdir)/mon2.cc $(srcdir)/$(GAME).hh $(srcdir)/sorcery.hh $(srcdir)/monsters.hh $(srcdir)/notify.hh $(srcdir)/objects.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
pmon2.o: $(srcdir)/pmon2.cc $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/monsters.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
-objects.o: $(srcdir)/objects.cc $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/objects.hh $(srcdir)/monsters.hh ./pobj_id.hh ./pmon_id.hh $(srcdir)/player.hh
+obj1.o: $(srcdir)/obj1.cc $(srcdir)/$(GAME).hh $(srcdir)/notify.hh $(srcdir)/objects.hh $(srcdir)/monsters.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
{
run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
}
- catch (Obumb_levgen_excep p)
+ catch (Game_levgen_excep p)
{
DEBUG_ARBITRARY(p.str);
}
{
run_random_walk(l, c, excavation_write, walk_data, pool_size);
}
- catch (Obumb_levgen_excep p)
+ catch (Game_levgen_excep p)
{
DEBUG_ARBITRARY(p.str);
}
{
run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
}
- catch (Obumb_levgen_excep p)
+ catch (Game_levgen_excep p)
{
DEBUG_ARBITRARY(p.str);
}
{
run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
}
- catch (Obumb_levgen_excep p)
+ catch (Game_levgen_excep p)
{
DEBUG_ARBITRARY(p.str);
}
{
run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
}
- catch (Obumb_levgen_excep p)
+ catch (Game_levgen_excep p)
{
DEBUG_ARBITRARY(p.str);
}
{
run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
}
- catch (Obumb_levgen_excep p)
+ catch (Game_levgen_excep p)
{
DEBUG_ARBITRARY(p.str);
}
{
run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
}
- catch (Obumb_levgen_excep p)
+ catch (Game_levgen_excep p)
{
DEBUG_ARBITRARY(p.str);
}
{
run_random_walk_unbounded(l, c, excavation_write, walk_data, LEVGEN_WALK_CELLS);
}
- catch (Obumb_levgen_excep p)
+ catch (Game_levgen_excep p)
{
DEBUG_ARBITRARY(p.str);
}
#define LAST_ROLE (Role_thanatophile)
#define NUM_ROLES (1 + LAST_ROLE)
-#define OBUMBRATA_MAGIC_NUMBER 0x0b756d62
+#define SAVEFILE_MAGIC_NUMBER 0x76696374
extern void game_cleanup(void);
-struct Obumb_sysexcep
+struct Game_sysexcep
{
int err; // errno value
- Obumb_sysexcep() = delete;
- Obumb_sysexcep(int e) : err(e) { }
+ Game_sysexcep() = delete;
+ Game_sysexcep(int e) : err(e) { }
};
-struct Obumb_dataexcep
+struct Game_dataexcep
{
char const *str;
- Obumb_dataexcep() = delete;
- Obumb_dataexcep(char const *s) : str(s) { }
+ Game_dataexcep() = delete;
+ Game_dataexcep(char const *s) : str(s) { }
};
extern bool game_finished;
DEPTH 1
DAMAGEABLE
MELEE_WEAPON
+DAGGER
WEAPON long sword
PLURAL long swords
-DESC A steel sword of simple but sturdy construction; the blade is three feet long.
+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
ASCII ')'
UTF8 ")"
DEPTH 4
DAMAGEABLE
MELEE_WEAPON
+SWORD
+
+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
+ASCII ')'
+UTF8 ")"
+COLOUR white
+POWER 13
+POWER2 0
+DEPTH 10
+DAMAGEABLE
+MELEE_WEAPON
+NOTIFY_EQUIP
+FLIGHT
+SWORD
+
+WEAPON runesword
+PLURAL runeswords
+DESC An eerily glowing long sword engraved with many strange runes.
+RARITY 80
+ASCII ')'
+UTF8 ")"
+COLOUR l_cyan
+POWER 20
+POWER2 0
+DEPTH 12
+DAMAGEABLE
+MELEE_WEAPON
+SWORD
WEAPON mace
PLURAL maces
DEPTH 2
DAMAGEABLE
MELEE_WEAPON
+BLUDGEON
-WEAPON runesword
-PLURAL runeswords
-DESC An eerily glowing sword engraved with many strange runes.
-RARITY 80
+WEAPON spear
+PLURAL spears
+DESC A blade on a stick.
+RARITY 50
ASCII ')'
UTF8 ")"
-COLOUR l_cyan
-POWER 20
+COLOUR brown
+POWER 8
POWER2 0
-DEPTH 12
+DEPTH 3
DAMAGEABLE
MELEE_WEAPON
+POLEARM
WEAPON hellglaive
PLURAL hellglaives
DAMAGEABLE
BREAK_REACT
MELEE_WEAPON
+DEMONIC
+POLEARM
WEAPON plague scythe
PLURAL plague sycthes
DEPTH 30
NOTIFY_EQUIP
MELEE_WEAPON
+DEMONIC
+POLEARM
WEAPON tormentor's lash
PLURAL tormentor's lashes
DAMAGEABLE
BREAK_REACT
MELEE_WEAPON
+DEMONIC
+WHIP
WEAPON death staff
PLURAL death staves
DEPTH 30
NOTIFY_EQUIP
MELEE_WEAPON
+BLUDGEON
WEAPON staff of fire
PLURAL staves of fire
DAMAGEABLE
ACTIVATABLE
MELEE_WEAPON
+BLUDGEON
WEAPON bow
PLURAL bows
DEPTH 1
DAMAGEABLE
RANGED_WEAPON
+
WEAPON crossbow
PLURAL crossbows
DESC A crossbow.
POWER2 10
DEPTH 1
DAMAGEABLE
+LEATHERY
ARMOUR chainmail
PLURAL suits of chainmail
POWER2 5
DEPTH 1
DAMAGEABLE
+ROBE
ARMOUR robe of swiftness
PLURAL robes of swiftness
POWER2 0
DEPTH 8
DAMAGEABLE
+SPEED
+ROBE
ARMOUR robe of shadows
PLURAL robes of shadows
POWER2 -15
DEPTH 18
DAMAGEABLE
+ROBE
ARMOUR dragonhide armour
PLURAL suits of dragonhide armour
POWER2 10
DEPTH 21
DAMAGEABLE
+RES_FIRE
+LEATHERY
ARMOUR meteoric plate armour
PLURAL suits of meteoric plate armour
POWER2 40
DEPTH 27
DAMAGEABLE
+RES_FIRE
ARMOUR sacred chainmail
PLURAL suits of sacred chainmail
COLOUR green
POWER 3
POWER2 10
-DEPTH 1
+DEPTH 3
DRESS
DAMAGEABLE
BREAK_REACT
NOTIFY_EQUIP
DAMAGEABLE
BREAK_REACT
+RES_POISON
+DEMONIC
+ROBE
ARMOUR lich's robe
PLURAL lich's robes
POWER2 0
DEPTH 30
NOTIFY_EQUIP
+RES_NECRO
+ROBE
ARMOUR infernite armour
PLURAL suits of infernite armour
POWER2 20
DEPTH 30
NOTIFY_EQUIP
+RES_FIRE
+DEMONIC
# Mr Read would like to note that he invented the following item and its
# game mechanics *before* becoming aware of the anime series _Kill la Kill_.
POWER 0
POWER2 0
DEPTH 1
+RES_FIRE
+DMG_FIRE
RING vampire ring
PLURAL vampire rings
POWER 0
POWER2 0
DEPTH 12
+RES_NECRO
+DMG_NECRO
RING frost ring
PLURAL frost rings
POWER 0
POWER2 0
DEPTH 1
+RES_COLD
+DMG_COLD
+PASS_WATER
RING teleport ring
PLURAL teleport rings
POWER 0
POWER2 0
DEPTH 15
+RES_POISON
+DEMONIC
RING protection ring
PLURAL protection rings
POWER 0
POWER2 0
DEPTH 15
+PROTECTIVE
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 imprisoned in this dismal place. Of the nature of its power, there are not even rumours.
+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
ASCII '='
UTF8 "="
ASCII '%'
UTF8 "%"
COLOUR brown
-POWER 0
+POWER 1500
POWER2 0
DEPTH 1
STACKABLE
ASCII '%'
UTF8 "%"
COLOUR yellow
-POWER 0
+POWER 1500
POWER2 0
DEPTH 1
STACKABLE
ASCII '%'
UTF8 "%"
COLOUR white
-POWER 0
+POWER 1500
POWER2 0
DEPTH 1
STACKABLE
POWER2 0
DEPTH 1
STACKABLE
+DEMONIC
CARRION corpse
PLURAL corpses
# 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
+
+monster sybarite
+desc A thrill-seeking devotee of fell powers.
+ascii 'f'
+utf8 "f"
+colour l_purple
+rarity 30
+power 18
+hp 120
+mhit 30
+mdam 18
+defence 20
+experience 600
+speed 1
+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.
-ascii 'I'
-utf8 "I"
+ascii 'E'
+utf8 "E"
colour white
rarity 50
power 6
RESIST_COLD
ARCHER
HUMANOID
+MADE_OF_ICE
+
+monster blood elemental
+desc A heaving shape of blood in the form of a breaking wave.
+ascii 'E'
+utf8 "E"
+colour red
+rarity 50
+power 15
+hp 100
+mhit 15
+mdam 20
+defence 10
+experience 150
+CONTAINS_BLOOD
+BURSTS_ON_DEATH
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
load_game();
done = true;
}
- catch (Obumb_sysexcep e)
+ catch (Game_sysexcep e)
{
mvwprintw(fullscreen_window, 14, 20, "Error opening saved game:\n");
mvwprintw(fullscreen_window, 15, 1, "%s\n", strerror(e.err));
}
- catch (Obumb_dataexcep e)
+ catch (Game_dataexcep e)
{
mvwprintw(fullscreen_window, 14, 20, "Error loading saved game:\n");
mvwprintw(fullscreen_window, 15, 1, "%s\n", e.str);
Offset delta = inner_br - inner_tl;
if ((delta.y | delta.x) & 1)
{
- throw Obumb_levgen_excep("Awkward dimensions for pillar room");
+ throw Game_levgen_excep("Awkward dimensions for pillar room");
}
excavate_normal_room(l, inner_tl, inner_br, region);
/* Second pass: What pattern of pillars?
int i = system(cmd);
if (i != 0)
{
- throw Obumb_sysexcep(errno);
+ throw Game_sysexcep(errno);
}
}
{
if (ferror(fp))
{
- throw Obumb_sysexcep(errno);
+ throw Game_sysexcep(errno);
}
else if (feof(fp))
{
- throw Obumb_dataexcep("Unexpected end of file");
+ throw Game_dataexcep("Unexpected end of file");
}
else
{
- throw Obumb_sysexcep(errno);
+ throw Game_sysexcep(errno);
}
}
}
{
if (mon >= first_free_mon_handle)
{
- throw Obumb_dataexcep("Object handle >= first free object handle - save game probably corrupt");
+ throw Game_dataexcep("Object handle >= first free object handle - save game probably corrupt");
}
}
{
if ((pm < 0) || (pm >= NUM_OF_PERMONS))
{
- throw Obumb_dataexcep("Monster permon ID out of bounds - save game probably corrupt");
+ throw Game_dataexcep("Monster permon ID out of bounds - save game probably corrupt");
}
}
{
if ((po < 0) || (po >= NUM_OF_PERMOBJS))
{
- throw Obumb_dataexcep("Object permobj ID out of bounds - save game probably corrupt");
+ throw Game_dataexcep("Object permobj ID out of bounds - save game probably corrupt");
}
}
{
if (obj >= first_free_obj_handle)
{
- throw Obumb_dataexcep("Object handle >= first free object handle - save game probably corrupt");
+ throw Game_dataexcep("Object handle >= first free object handle - save game probably corrupt");
}
}
}
if ((i >= l->chunks_high) || (j >= l->chunks_wide))
{
- throw Obumb_dataexcep("Out-of-bounds chunk indices while loading level - save probably corrupt");
+ throw Game_dataexcep("Out-of-bounds chunk indices while loading level - save probably corrupt");
}
l->chunks[i][j] = new Chunk(l->dead_space);
deserialize_chunk(fp, l->chunks[i][j]);
close(fd);
return;
}
- serialize_uint32(fp, OBUMBRATA_MAGIC_NUMBER);
+ serialize_uint32(fp, SAVEFILE_MAGIC_NUMBER);
serialize_uint32(fp, MAJVERS);
serialize_uint32(fp, MINVERS);
serialize_int(fp, depth);
static void check_magic(uint32_t magic)
{
- if (magic != OBUMBRATA_MAGIC_NUMBER)
+ if (magic != SAVEFILE_MAGIC_NUMBER)
{
- throw Obumb_dataexcep("Invalid file ID code");
+ throw Game_dataexcep("Invalid file ID code");
}
}
{
if (minvers != MINVERS)
{
- throw Obumb_dataexcep("Minor-version mismatch");
+ throw Game_dataexcep("Minor-version mismatch");
}
}
{
if (majvers != MAJVERS)
{
- throw Obumb_dataexcep("Major-version mismatch");
+ throw Game_dataexcep("Major-version mismatch");
}
}
fd = openat(datadir_fd, "obumbrata.sav", O_RDONLY, 0);
if (fd == -1)
{
- throw Obumb_sysexcep(errno);
+ throw Game_sysexcep(errno);
}
fp = fdopen(fd, "rb");
if (!fp)
{
- throw Obumb_sysexcep(errno);
+ throw Game_sysexcep(errno);
}
deserialize_uint32(fp, &tmp);
check_magic(tmp);
deserialize_objhandle(fp, &first_free_obj_handle);
if (first_free_obj_handle == 0)
{
- throw Obumb_dataexcep("Object pool exhausted - save file probably corrupt");
+ throw Game_dataexcep("Object pool exhausted - save file probably corrupt");
}
deserialize_monhandle(fp, &first_free_mon_handle);
if (first_free_mon_handle == 0)
{
- throw Obumb_dataexcep("Monster pool exhausted - save file probably corrupt");
+ throw Game_dataexcep("Monster pool exhausted - save file probably corrupt");
}
deserialize_player(fp, &u);
deserialize_level(fp, &lvl);
}
return;
}
- catch (Obumb_dataexcep e)
+ catch (Game_dataexcep e)
{
/* NOTE: if we got a dataexcep, we've definitely opened fp, so no need
* to check for null pointer */
game_cleanup();
throw;
}
- catch (Obumb_sysexcep e)
+ catch (Game_sysexcep e)
{
if (fp)
{
}
if (bailout < 1)
{
- throw Obumb_levgen_excep("run_random_walk exceeded bailout limit");
+ throw Game_levgen_excep("run_random_walk exceeded bailout limit");
}
return c;
}
}
if (bailout < 1)
{
- throw Obumb_levgen_excep("run_random_walk exceeded bailout limit");
+ throw Game_levgen_excep("run_random_walk exceeded bailout limit");
}
return c;
}
Coord run_random_walk_unbounded(Level *l, Coord oc, rwalk_mod_funcptr func,
void const *priv_ptr, int cells);
-struct Obumb_levgen_excep
+struct Game_levgen_excep
{
char const *str;
- Obumb_levgen_excep() = delete;
- Obumb_levgen_excep(char const *s) : str(s) { }
+ Game_levgen_excep() = delete;
+ Game_levgen_excep(char const *s) : str(s) { }
};
#endif
--- /dev/null
+/*! \file monsters.cc
+ * \brief Monster-related functions
+ */
+
+/* Copyright 2005-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 MONSTERS_CC
+#include "victrix-abyssi.hh"
+#include "monsters.hh"
+#include "objects.hh"
+
+const Mon_handle NO_MON = 0u;
+
+std::map<Mon_handle, Mon> monsters;
+static bool reject_mon(int pm);
+
+/*! \brief Summon some monsters
+ *
+ * \return Number of monsters summoned
+ * \param how_many Maximum number of monsters to summon
+ */
+int summoning(Coord c, int how_many)
+{
+ int i;
+ Offset delta;
+ Coord testpos;
+ int tryct;
+ Mon_handle mon;
+ int created = 0;
+ int pmon;
+ for (i = 0; i < how_many; i++)
+ {
+ for (tryct = 0; tryct < 20; tryct++)
+ {
+ delta = random_step();
+ testpos = c + delta;
+ if ((lvl.terrain_at(testpos) == FLOOR) &&
+ (lvl.mon_at(testpos) == NO_MON) &&
+ (testpos != u.pos))
+ {
+ pmon = get_random_pmon();
+ if (pmon_is_magician(pmon))
+ {
+ /* Never summon magicians! */
+ continue;
+ }
+ mon = create_mon(NO_PMON, testpos);
+ if (mon != NO_MON)
+ {
+ created++;
+ break;
+ }
+ }
+ }
+ }
+ return created;
+}
+
+int ood(int power, int ratio)
+{
+ return (depth - power + ratio - 1) / ratio;
+}
+
+int get_random_pmon(void)
+{
+ int tryct;
+ int pm;
+ for (tryct = 0; tryct < 200; tryct++)
+ {
+ pm = zero_die(NUM_OF_PERMONS);
+ if (reject_mon(pm))
+ {
+ pm = NO_PMON;
+ continue;
+ }
+ break;
+ }
+ return pm;
+}
+
+Mon_handle first_free_mon_handle = 1u;
+static Mon_handle get_first_free_mon(void)
+{
+ if (first_free_mon_handle == NO_MON)
+ {
+ return NO_MON;
+ }
+ return first_free_mon_handle++;
+}
+
+Mon_handle create_mon(int pm_idx, Coord c)
+{
+ Mon_handle mon;
+ if (lvl.mon_at(c) != NO_MON)
+ {
+ debug_create_mon_occupied(c);
+ return NO_MON;
+ }
+ if (pm_idx == NO_PMON)
+ {
+ pm_idx = get_random_pmon();
+ if (pm_idx == NO_PMON)
+ {
+ debug_pmon_select_failed();
+ return NO_MON;
+ }
+ }
+ mon = get_first_free_mon();
+ if (mon != NO_MON)
+ {
+ Mon m;
+ m.self = mon;
+ m.pm_ref = pm_idx;
+ m.flags = MF_USED | MF_ALIVE;
+ m.pos = c;
+ m.ai_lastpos = Nowhere;
+ m.hpmax = permons[pm_idx].hp + ood(permons[pm_idx].power, 1);
+ m.hpcur = m.hpmax;
+ m.mtohit = permons[pm_idx].mtohit + ood(permons[pm_idx].power, 3);
+ m.defence = permons[pm_idx].defence + ood(permons[pm_idx].power, 3);
+ m.mdam = permons[pm_idx].mdam + ood(permons[pm_idx].power, 5);
+ if (permons[pm_idx].rdam != NO_ATK)
+ {
+ m.rtohit = permons[pm_idx].rtohit + ood(permons[pm_idx].power, 3);
+ m.rdam = permons[pm_idx].rdam + ood(permons[pm_idx].power, 5);
+ }
+ else
+ {
+ m.rtohit = NO_ATK;
+ m.rdam = NO_ATK;
+ }
+ monsters[mon] = m;
+ lvl.set_mon_at(c, mon);
+ if (mon_visible(mon))
+ {
+ notify_new_mon_at(c, mon);
+ }
+ return mon;
+ }
+ return NO_MON;
+}
+
+/*! \brief Handle a monster's death drop. */
+void death_drop(Mon_handle mon)
+{
+ Mon const *mptr = mon_snap(mon);
+ for (int i = 0; death_drops[i].func != nullptr; ++i)
+ {
+ if (death_drops[i].pm_ref == mptr->pm_ref)
+ {
+ death_drops[i].func(mptr->pos);
+ }
+ }
+}
+
+bool Mon::can_pass(Coord c) const
+{
+ Terrain terr;
+ if (!lvl.in_bounds(c))
+ {
+ return false;
+ }
+ if (lvl.mon_at(c) != NO_MON)
+ {
+ return false;
+ }
+ if (c == u.pos)
+ {
+ /* Sanity check! */
+ return false;
+ }
+ if (is_ethereal())
+ {
+ return true;
+ }
+ terr = lvl.terrain_at(c);
+ if (terrain_blocks_beings(terr))
+ {
+ /* Let's *not* stuff all the wall types into a switch, eh? */
+ return false;
+ }
+ if (terrain_is_hot(terr) && !can_fly() && !resists(DT_FIRE))
+ {
+ return false;
+ }
+ if (terrain_drowns(terr) && !can_fly() && !resists(DT_DROWNING))
+ {
+ return false;
+ }
+ if (terrain_gapes(terr) && !can_fly())
+ {
+ return false;
+ }
+ return true;
+}
+
+void heal_mon(Mon_handle mon, int amount, int cansee)
+{
+ if (amount > (monsters[mon].hpmax - monsters[mon].hpcur))
+ {
+ amount = monsters[mon].hpmax - monsters[mon].hpcur;
+ }
+ if (amount > 0)
+ {
+ if (cansee)
+ {
+ notify_mon_healed(mon);
+ }
+ monsters[mon].hpcur += amount;
+ }
+}
+
+void unplace_mon(Mon_handle mon)
+{
+ lvl.set_mon_at(monsters[mon].pos, NO_MON);
+ monsters[mon].flags &= ~MF_USED;
+}
+
+void apply_liquid(int pm, Coord c)
+{
+ Decal_tag decal = pmon_fluid_decal(pm);
+ if (pm != NO_DECAL)
+ {
+ lvl.set_decal_at(c, decal);
+ }
+}
+
+/*! \brief Handle the death of a monster
+ *
+ * \todo Support special effects on monster death
+ */
+void kill_mon(Mon_handle mon, bool by_you, bool explode_corpse)
+{
+ Mon *mptr = mon_snapv(mon);
+ Terrain t = lvl.terrain_at(mptr->pos);
+ int pm = mptr->pm_ref;
+ bool detonate =
+ (pmon_explodes(pm) || (pmon_leaves_corpse(pm) && explode_corpse));
+ if (!terrain_blocks_items(t))
+ {
+ apply_liquid(pm, mptr->pos);
+ }
+ if (detonate)
+ {
+ detonate_mon(mon);
+ }
+ else if (pmon_leaves_corpse(pm))
+ {
+ create_corpse(pm, mptr->pos);
+ }
+ death_drop(mon); // phat lewt!
+ mptr->flags &= ~MF_ALIVE;
+ monsters[mon].hpcur = -1;
+ unplace_mon(mon); // cleanup
+ if (by_you)
+ {
+ notify_player_killed_mon(mon);
+ gain_experience(permons[monsters[mon].pm_ref].exp);
+ }
+ else if (mon_visible(mon) && !detonate)
+ {
+ notify_mon_dies(mon);
+ }
+}
+
+void damage_mon(Mon_handle mon, int amount, bool by_you)
+{
+ Mon *mptr;
+ mptr = mon_snapv(mon);
+ if (amount >= mptr->hpcur)
+ {
+ kill_mon(mon, by_you);
+ }
+ else
+ {
+ mptr->hpcur -= amount;
+ }
+}
+
+bool reject_mon(int pm)
+{
+ if ((permons[pm].power > depth) || (zero_die(100) < permons[pm].rarity))
+ {
+ return true;
+ }
+ return false;
+}
+
+Pass_fail teleport_mon_to_you(Mon_handle mon)
+{
+ int tryct;
+ Offset delta;
+ Coord c;
+ int success = 0;
+ Mon *mptr = mon_snapv(mon);
+ Coord oldpos = mptr->pos;
+ for (tryct = 0; tryct < 40; tryct++)
+ {
+ delta = random_step();
+ c = u.pos + delta;
+ if (mptr->can_pass(c))
+ {
+ success = 1;
+ break;
+ }
+ }
+ if (success)
+ {
+ reloc_mon(mon, c);
+ notify_mon_appears(mon);
+ return You_pass;
+ }
+ return You_fail;
+}
+
+Pass_fail teleport_mon(Mon_handle mon)
+{
+ Pass_fail rval = You_fail;
+ int cell_try;
+ Coord c;
+ for (cell_try = 0; cell_try < 200; cell_try++)
+ {
+ c = lvl.random_point(1);
+ if ((lvl.mon_at(c) == NO_MON) && (lvl.terrain_at(c) == FLOOR) && (c != u.pos))
+ {
+ reloc_mon(mon, c);
+ rval = You_pass;
+ break;
+ }
+ }
+ return rval;
+}
+
+int knockback_mon(Mon_handle mon, Offset step, bool cansee, bool by_you)
+{
+ /* 0 = blocked, 1 = knocked, 2 = killed */
+ Mon *mptr = mon_snapv(mon);
+ Coord c = mptr->pos + step;
+ Coord savedpos = mptr->pos;
+ Terrain terr = lvl.terrain_at(c);
+
+ if (mptr->resists(DT_KNOCKBACK))
+ {
+ if (cansee)
+ {
+ notify_knockback_mon_resisted(mon);
+ }
+ return 0;
+ }
+ if (terrain_blocks_beings(terr))
+ {
+ if (cansee)
+ {
+ notify_knockback_mon_blocked(mon);
+ }
+ return 0;
+ }
+ reloc_mon(mon, c);
+ switch (terr)
+ {
+ case LAVA:
+ if (mptr->can_fly())
+ {
+ if (cansee)
+ {
+ notify_knockback_mon_hover_lava(mon);
+ }
+ }
+ else
+ {
+ if (cansee)
+ {
+ notify_knockback_mon_immersed_lava(mon);
+ }
+ else
+ {
+ notify_knockback_mon_lava_offscreen();
+ }
+ if (!mptr->resists(DT_FIRE))
+ {
+ kill_mon(mon, by_you);
+ return 2;
+ }
+ }
+ break;
+ case WATER:
+ if (mptr->can_fly())
+ {
+ if (cansee)
+ {
+ notify_knockback_mon_hover_water(mon);
+ }
+ }
+ else
+ {
+ if (cansee)
+ {
+ notify_knockback_mon_immersed_water(mon);
+ }
+ else
+ {
+ notify_knockback_mon_water_offscreen();
+ }
+ if (!mptr->resists(DT_DROWNING))
+ {
+ kill_mon(mon, by_you);
+ return 2;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return 1;
+}
+
+void reloc_mon(Mon_handle mon, Coord newpos)
+{
+ Mon *mptr = mon_snapv(mon);
+ lvl.set_mon_at(mptr->pos, NO_MON);
+ notify_new_mon_at(mptr->pos, NO_MON);
+ mptr->pos = newpos;
+ lvl.set_mon_at(mptr->pos, mon);
+ notify_new_mon_at(mptr->pos, mon);
+}
+
+void move_mon(Mon_handle mon, Coord c)
+{
+ Mon *mptr;
+ mptr = mon_snapv(mon);
+ if (!mptr->can_pass(c))
+ {
+ debug_mon_invalid_move(mon, c);
+ return;
+ }
+ if (lvl.mon_at(mptr->pos) != mon)
+ {
+ debug_misplaced_monster();
+ return;
+ }
+ reloc_mon(mon, c);
+}
+
+void summon_demon_near(Coord c)
+{
+ Coord c2 = c + random_step();
+ Mon_handle mon;
+ if ((lvl.terrain_at(c2) == FLOOR) && (lvl.mon_at(c2) == NO_MON) &&
+ (c2 != u.pos))
+ {
+ mon = create_mon(PM_DEMON, c2);
+ notify_summon_demon(mon);
+ }
+}
+
+void detonate_mon(Mon_handle mon)
+{
+ Mon *mptr = mon_snapv(mon);
+ Coord c;
+ int pm = mptr->pm_ref;
+ Decal_tag decal = pmon_fluid_decal(pm);
+ notify_mon_detonates(mon, decal);
+ if (decal != NO_DECAL)
+ {
+ for (c.y = mptr->pos.y - 1; c.y <= mptr->pos.y + 1; ++c.y)
+ {
+ for (c.x = mptr->pos.x - 1; c.x <= mptr->pos.x + 1; ++c.x)
+ {
+ if (lvl.in_bounds(c))
+ {
+ lvl.set_decal_at(c, decal);
+ }
+ }
+ }
+ }
+ return;
+}
+
+bool mon_visible(Mon_handle mon)
+{
+ Offset delta;
+ Mon const *mptr = mon_snap(mon);
+ if (!(mptr->flags & MF_USED))
+ {
+ return false;
+ }
+ delta = mptr->pos.delta(u.pos);
+ if (delta.len_cheb() <= MAX_FOV_RADIUS)
+ {
+ return (player_fov.affected[MAX_FOV_RADIUS + delta.y][MAX_FOV_RADIUS + delta.x]) & Visflag_central;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void update_mon(Mon_handle mon)
+{
+ int cansee;
+ Mon *mptr = mon_snapv(mon);
+ if (mptr->hpcur < mptr->hpmax)
+ {
+ cansee = mon_visible(mon);
+ // TODO modify regen handling to use flags/fields instead of switching on pm_ref
+ switch (mptr->pm_ref)
+ {
+ case PM_TROLL:
+ if (!(game_tick % 10))
+ {
+ if (cansee)
+ {
+ notify_mon_regenerates(mon);
+ }
+ heal_mon(mon, one_die(3) + 3, 0);
+ }
+ break;
+
+ case PM_ZOMBIE:
+ /* Zombies don't recover from their injuries. */
+ break;
+
+ default:
+ if (!(game_tick % 20))
+ {
+ heal_mon(mon, 1, cansee);
+ }
+ break;
+ }
+ }
+}
+
+bool Mon::resists(Damtyp dt) const
+{
+ switch (dt)
+ {
+ case DT_COLD:
+ return pmon_resists_cold(pm_ref);
+ case DT_FIRE:
+ return pmon_resists_fire(pm_ref);
+ case DT_POISON:
+ return pmon_resists_poison(pm_ref);
+ case DT_NECRO:
+ return pmon_resists_necro(pm_ref);
+ case DT_ELEC:
+ return pmon_resists_elec(pm_ref);
+ case DT_KNOCKBACK:
+ return pmon_resists_knockback(pm_ref);
+ case DT_DROWNING:
+ return pmon_resists_drowning(pm_ref);
+ default:
+ return false;
+ }
+}
+
+bool Mon::is_ethereal(void) const
+{
+ return pmon_is_ethereal(pm_ref);
+}
+
+bool Mon::can_fly(void) const
+{
+ return pmon_can_fly(pm_ref);
+}
+
+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:expandtab
+++ /dev/null
-/*! \file monsters.cc
- * \brief Monster-related functions
- */
-
-/* Copyright 2005-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 MONSTERS_CC
-#include "victrix-abyssi.hh"
-#include "monsters.hh"
-#include "objects.hh"
-
-const Mon_handle NO_MON = 0u;
-
-std::map<Mon_handle, Mon> monsters;
-static bool reject_mon(int pm);
-
-/*! \brief Summon some monsters
- *
- * \return Number of monsters summoned
- * \param how_many Maximum number of monsters to summon
- */
-int summoning(Coord c, int how_many)
-{
- int i;
- Offset delta;
- Coord testpos;
- int tryct;
- Mon_handle mon;
- int created = 0;
- int pmon;
- for (i = 0; i < how_many; i++)
- {
- for (tryct = 0; tryct < 20; tryct++)
- {
- delta = random_step();
- testpos = c + delta;
- if ((lvl.terrain_at(testpos) == FLOOR) &&
- (lvl.mon_at(testpos) == NO_MON) &&
- (testpos != u.pos))
- {
- pmon = get_random_pmon();
- if (pmon_is_magician(pmon))
- {
- /* Never summon magicians! */
- continue;
- }
- mon = create_mon(NO_PMON, testpos);
- if (mon != NO_MON)
- {
- created++;
- break;
- }
- }
- }
- }
- return created;
-}
-
-int ood(int power, int ratio)
-{
- return (depth - power + ratio - 1) / ratio;
-}
-
-int get_random_pmon(void)
-{
- int tryct;
- int pm;
- for (tryct = 0; tryct < 200; tryct++)
- {
- pm = zero_die(NUM_OF_PERMONS);
- if (reject_mon(pm))
- {
- pm = NO_PMON;
- continue;
- }
- break;
- }
- return pm;
-}
-
-Mon_handle first_free_mon_handle = 1u;
-static Mon_handle get_first_free_mon(void)
-{
- if (first_free_mon_handle == NO_MON)
- {
- return NO_MON;
- }
- return first_free_mon_handle++;
-}
-
-Mon_handle create_mon(int pm_idx, Coord c)
-{
- Mon_handle mon;
- if (lvl.mon_at(c) != NO_MON)
- {
- debug_create_mon_occupied(c);
- return NO_MON;
- }
- if (pm_idx == NO_PMON)
- {
- pm_idx = get_random_pmon();
- if (pm_idx == NO_PMON)
- {
- debug_pmon_select_failed();
- return NO_MON;
- }
- }
- mon = get_first_free_mon();
- if (mon != NO_MON)
- {
- Mon m;
- m.self = mon;
- m.pm_ref = pm_idx;
- m.flags = MF_USED | MF_ALIVE;
- m.pos = c;
- m.ai_lastpos = Nowhere;
- m.hpmax = permons[pm_idx].hp + ood(permons[pm_idx].power, 1);
- m.hpcur = m.hpmax;
- m.mtohit = permons[pm_idx].mtohit + ood(permons[pm_idx].power, 3);
- m.defence = permons[pm_idx].defence + ood(permons[pm_idx].power, 3);
- m.mdam = permons[pm_idx].mdam + ood(permons[pm_idx].power, 5);
- if (permons[pm_idx].rdam != NO_ATK)
- {
- m.rtohit = permons[pm_idx].rtohit + ood(permons[pm_idx].power, 3);
- m.rdam = permons[pm_idx].rdam + ood(permons[pm_idx].power, 5);
- }
- else
- {
- m.rtohit = NO_ATK;
- m.rdam = NO_ATK;
- }
- monsters[mon] = m;
- lvl.set_mon_at(c, mon);
- if (mon_visible(mon))
- {
- notify_new_mon_at(c, mon);
- }
- return mon;
- }
- return NO_MON;
-}
-
-/*! \brief Handle a monster's death drop. */
-void death_drop(Mon_handle mon)
-{
- Mon const *mptr = mon_snap(mon);
- for (int i = 0; death_drops[i].func != nullptr; ++i)
- {
- if (death_drops[i].pm_ref == mptr->pm_ref)
- {
- death_drops[i].func(mptr->pos);
- }
- }
-}
-
-bool Mon::can_pass(Coord c) const
-{
- Terrain terr;
- if (!lvl.in_bounds(c))
- {
- return false;
- }
- if (lvl.mon_at(c) != NO_MON)
- {
- return false;
- }
- if (c == u.pos)
- {
- /* Sanity check! */
- return false;
- }
- if (is_ethereal())
- {
- return true;
- }
- terr = lvl.terrain_at(c);
- if (terrain_blocks_beings(terr))
- {
- /* Let's *not* stuff all the wall types into a switch, eh? */
- return false;
- }
- if (terrain_is_hot(terr) && !can_fly() && !resists(DT_FIRE))
- {
- return false;
- }
- if (terrain_drowns(terr) && !can_fly() && !resists(DT_DROWNING))
- {
- return false;
- }
- if (terrain_gapes(terr) && !can_fly())
- {
- return false;
- }
- return true;
-}
-
-void heal_mon(Mon_handle mon, int amount, int cansee)
-{
- if (amount > (monsters[mon].hpmax - monsters[mon].hpcur))
- {
- amount = monsters[mon].hpmax - monsters[mon].hpcur;
- }
- if (amount > 0)
- {
- if (cansee)
- {
- notify_mon_healed(mon);
- }
- monsters[mon].hpcur += amount;
- }
-}
-
-void unplace_mon(Mon_handle mon)
-{
- lvl.set_mon_at(monsters[mon].pos, NO_MON);
- monsters[mon].flags &= ~MF_USED;
-}
-
-void apply_liquid(int pm, Coord c)
-{
- Decal_tag decal = pmon_fluid_decal(pm);
- if (pm != NO_DECAL)
- {
- lvl.set_decal_at(c, decal);
- }
-}
-
-/*! \brief Handle the death of a monster
- *
- * \todo Support special effects on monster death
- */
-void kill_mon(Mon_handle mon, bool by_you, bool explode_corpse)
-{
- Mon *mptr = mon_snapv(mon);
- Terrain t = lvl.terrain_at(mptr->pos);
- int pm = mptr->pm_ref;
- bool detonate =
- (pmon_explodes(pm) || (pmon_leaves_corpse(pm) && explode_corpse));
- if (!terrain_blocks_items(t))
- {
- apply_liquid(pm, mptr->pos);
- }
- if (detonate)
- {
- detonate_mon(mon);
- }
- else if (pmon_leaves_corpse(pm))
- {
- create_corpse(pm, mptr->pos);
- }
- death_drop(mon); // phat lewt!
- mptr->flags &= ~MF_ALIVE;
- monsters[mon].hpcur = -1;
- unplace_mon(mon); // cleanup
- if (by_you)
- {
- notify_player_killed_mon(mon);
- gain_experience(permons[monsters[mon].pm_ref].exp);
- }
- else if (mon_visible(mon) && !detonate)
- {
- notify_mon_dies(mon);
- }
-}
-
-void damage_mon(Mon_handle mon, int amount, bool by_you)
-{
- Mon *mptr;
- mptr = mon_snapv(mon);
- if (amount >= mptr->hpcur)
- {
- kill_mon(mon, by_you);
- }
- else
- {
- mptr->hpcur -= amount;
- }
-}
-
-bool reject_mon(int pm)
-{
- if ((permons[pm].power > depth) || (zero_die(100) < permons[pm].rarity))
- {
- return true;
- }
- return false;
-}
-
-Pass_fail teleport_mon_to_you(Mon_handle mon)
-{
- int tryct;
- Offset delta;
- Coord c;
- int success = 0;
- Mon *mptr = mon_snapv(mon);
- Coord oldpos = mptr->pos;
- for (tryct = 0; tryct < 40; tryct++)
- {
- delta = random_step();
- c = u.pos + delta;
- if (mptr->can_pass(c))
- {
- success = 1;
- break;
- }
- }
- if (success)
- {
- reloc_mon(mon, c);
- notify_mon_appears(mon);
- return You_pass;
- }
- return You_fail;
-}
-
-Pass_fail teleport_mon(Mon_handle mon)
-{
- Pass_fail rval = You_fail;
- int cell_try;
- Coord c;
- for (cell_try = 0; cell_try < 200; cell_try++)
- {
- c = lvl.random_point(1);
- if ((lvl.mon_at(c) == NO_MON) && (lvl.terrain_at(c) == FLOOR) && (c != u.pos))
- {
- reloc_mon(mon, c);
- rval = You_pass;
- break;
- }
- }
- return rval;
-}
-
-int knockback_mon(Mon_handle mon, Offset step, bool cansee, bool by_you)
-{
- /* 0 = blocked, 1 = knocked, 2 = killed */
- Mon *mptr = mon_snapv(mon);
- Coord c = mptr->pos + step;
- Coord savedpos = mptr->pos;
- Terrain terr = lvl.terrain_at(c);
-
- if (mptr->resists(DT_KNOCKBACK))
- {
- if (cansee)
- {
- notify_knockback_mon_resisted(mon);
- }
- return 0;
- }
- if (terrain_blocks_beings(terr))
- {
- if (cansee)
- {
- notify_knockback_mon_blocked(mon);
- }
- return 0;
- }
- reloc_mon(mon, c);
- switch (terr)
- {
- case LAVA:
- if (mptr->can_fly())
- {
- if (cansee)
- {
- notify_knockback_mon_hover_lava(mon);
- }
- }
- else
- {
- if (cansee)
- {
- notify_knockback_mon_immersed_lava(mon);
- }
- else
- {
- notify_knockback_mon_lava_offscreen();
- }
- if (!mptr->resists(DT_FIRE))
- {
- kill_mon(mon, by_you);
- return 2;
- }
- }
- break;
- case WATER:
- if (mptr->can_fly())
- {
- if (cansee)
- {
- notify_knockback_mon_hover_water(mon);
- }
- }
- else
- {
- if (cansee)
- {
- notify_knockback_mon_immersed_water(mon);
- }
- else
- {
- notify_knockback_mon_water_offscreen();
- }
- if (!mptr->resists(DT_DROWNING))
- {
- kill_mon(mon, by_you);
- return 2;
- }
- }
- break;
- default:
- break;
- }
- return 1;
-}
-
-void reloc_mon(Mon_handle mon, Coord newpos)
-{
- Mon *mptr = mon_snapv(mon);
- lvl.set_mon_at(mptr->pos, NO_MON);
- notify_new_mon_at(mptr->pos, NO_MON);
- mptr->pos = newpos;
- lvl.set_mon_at(mptr->pos, mon);
- notify_new_mon_at(mptr->pos, mon);
-}
-
-void move_mon(Mon_handle mon, Coord c)
-{
- Mon *mptr;
- mptr = mon_snapv(mon);
- if (!mptr->can_pass(c))
- {
- debug_mon_invalid_move(mon, c);
- return;
- }
- if (lvl.mon_at(mptr->pos) != mon)
- {
- debug_misplaced_monster();
- return;
- }
- reloc_mon(mon, c);
-}
-
-void summon_demon_near(Coord c)
-{
- Coord c2 = c + random_step();
- Mon_handle mon;
- if ((lvl.terrain_at(c2) == FLOOR) && (lvl.mon_at(c2) == NO_MON) &&
- (c2 != u.pos))
- {
- mon = create_mon(PM_DEMON, c2);
- notify_summon_demon(mon);
- }
-}
-
-void detonate_mon(Mon_handle mon)
-{
- Mon *mptr = mon_snapv(mon);
- Coord c;
- int pm = mptr->pm_ref;
- Decal_tag decal = pmon_fluid_decal(pm);
- notify_mon_detonates(mon, decal);
- if (decal != NO_DECAL)
- {
- for (c.y = mptr->pos.y - 1; c.y <= mptr->pos.y + 1; ++c.y)
- {
- for (c.x = mptr->pos.x - 1; c.x <= mptr->pos.x + 1; ++c.x)
- {
- if (lvl.in_bounds(c))
- {
- lvl.set_decal_at(c, decal);
- }
- }
- }
- }
- return;
-}
-
-bool mon_visible(Mon_handle mon)
-{
- Offset delta;
- Mon const *mptr = mon_snap(mon);
- if (!(mptr->flags & MF_USED))
- {
- return false;
- }
- delta = mptr->pos.delta(u.pos);
- if (delta.len_cheb() <= MAX_FOV_RADIUS)
- {
- return (player_fov.affected[MAX_FOV_RADIUS + delta.y][MAX_FOV_RADIUS + delta.x]) & Visflag_central;
- }
- else
- {
- return false;
- }
-}
-
-void update_mon(Mon_handle mon)
-{
- int cansee;
- Mon *mptr = mon_snapv(mon);
- if (mptr->hpcur < mptr->hpmax)
- {
- cansee = mon_visible(mon);
- // TODO modify regen handling to use flags/fields instead of switching on pm_ref
- switch (mptr->pm_ref)
- {
- case PM_TROLL:
- if (!(game_tick % 10))
- {
- if (cansee)
- {
- notify_mon_regenerates(mon);
- }
- heal_mon(mon, one_die(3) + 3, 0);
- }
- break;
-
- case PM_ZOMBIE:
- /* Zombies don't recover from their injuries. */
- break;
-
- default:
- if (!(game_tick % 20))
- {
- heal_mon(mon, 1, cansee);
- }
- break;
- }
- }
-}
-
-bool Mon::resists(Damtyp dt) const
-{
- switch (dt)
- {
- case DT_COLD:
- return pmon_resists_cold(pm_ref);
- case DT_FIRE:
- return pmon_resists_fire(pm_ref);
- case DT_POISON:
- return pmon_resists_poison(pm_ref);
- case DT_NECRO:
- return pmon_resists_necro(pm_ref);
- case DT_ELEC:
- return pmon_resists_elec(pm_ref);
- case DT_KNOCKBACK:
- return pmon_resists_knockback(pm_ref);
- case DT_DROWNING:
- return pmon_resists_drowning(pm_ref);
- default:
- return false;
- }
-}
-
-bool Mon::is_ethereal(void) const
-{
- return pmon_is_ethereal(pm_ref);
-}
-
-bool Mon::can_fly(void) const
-{
- return pmon_can_fly(pm_ref);
-}
-
-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:expandtab
--- /dev/null
+/* \file objects.cc
+ * \brief Object handling
+ */
+
+/* Copyright 2005-2013 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 OBJECTS_CC
+#include "victrix-abyssi.hh"
+#include "objects.hh"
+#include "monsters.hh"
+
+#include <string.h>
+
+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)
+{
+ Obj *optr = obj_snapv(obj);
+ int i;
+ for (i = 0; scroll_table[i].read_func != nullptr; ++i)
+ {
+ if (scroll_table[i].pobj == optr->po_ref)
+ {
+ scroll_table[i].read_func(obj);
+ consume_obj(obj);
+ return Cost_std;
+ }
+ }
+ debug_read_non_scroll();
+ return Cost_none;
+}
+
+bool consume_obj(Obj_handle obj)
+{
+ int i;
+ Obj *optr = obj_snapv(obj);
+ optr->quan--;
+ if (optr->quan == 0)
+ {
+ optr->flags &= ~OF_USED;
+ if (optr->flags & OF_WITH_YOU)
+ {
+ if (obj == u.armour)
+ {
+ u.armour = NO_OBJ;
+ recalc_defence();
+ notify_armour_broke(obj);
+ }
+ else if (obj == u.weapon)
+ {
+ u.weapon = NO_OBJ;
+ recalc_defence();
+ notify_weapon_broke(obj);
+ }
+ else if (obj == u.ring)
+ {
+ u.ring = NO_OBJ;
+ recalc_defence();
+ }
+ for (i = 0; i < 19; i++)
+ {
+ if (u.inventory[i] == obj)
+ {
+ u.inventory[i] = NO_OBJ;
+ break;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+/*! \brief Consume a food */
+Action_cost eat_food(Obj_handle obj)
+{
+ Obj *optr = obj_snapv(obj);
+ bool ravenous = (u.food < 0);
+ if (permobjs[optr->po_ref].poclass != POCLASS_FOOD)
+ {
+ debug_eat_non_food(obj);
+ return Cost_none;
+ }
+ u.food += permobjs[optr->po_ref].power;
+ if (optr->po_ref == PO_DEVIL_SPLEEN)
+ {
+ notify_ingest_spleen();
+ if (zero_die(2))
+ {
+ gain_body(1);
+ }
+ else
+ {
+ gain_agility(1);
+ }
+ // TODO add more tracking state to this item so we don't have to randomize the fell power choice
+ ++u.sympathy[zero_die(TOTAL_FELL_POWERS)];
+ }
+ else
+ {
+ notify_eat_food(ravenous);
+ }
+ consume_obj(obj);
+ return Cost_std;
+}
+
+/*! \brief Consume a potion */
+Action_cost quaff_potion(Obj_handle obj)
+{
+ Obj *optr = obj_snapv(obj);
+ int i;
+ for (i = 0; potion_table[i].pobj != NO_POBJ; ++i)
+ {
+ if (potion_table[i].pobj == optr->po_ref)
+ {
+ potion_table[i].quaff_func(obj);
+ consume_obj(obj);
+ return Cost_std;
+ }
+ }
+ debug_quaff_non_potion();
+ return Cost_none;
+}
+
+Obj_handle first_free_obj_handle = 1u;
+
+Obj_handle get_first_free_obj(void)
+{
+ if (first_free_obj_handle == 0u)
+ {
+ return NO_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;
+ Coord nc = c;
+ int panic_count = 200;
+ while ((terrain_blocks_items(lvl.terrain_at(nc)) || (lvl.obj_at(c) != NO_OBJ)) && (panic_count > 0))
+ {
+ do
+ {
+ delta = random_step();
+ nc = c + delta;
+ }
+ while (terrain_blocks_items(lvl.terrain_at(nc)));
+ c = nc;
+ --panic_count;
+ }
+ if (panic_count < 1)
+ {
+ 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();
+ if (obj == NO_OBJ)
+ {
+ debug_object_pool_exhausted();
+ return NO_OBJ;
+ }
+ if (po_idx == NO_POBJ)
+ {
+ po_idx = get_random_pobj();
+ if (po_idx == NO_POBJ)
+ {
+ debug_pobj_select_failed();
+ return NO_OBJ;
+ }
+ }
+ Obj o;
+ memset(o.meta, '\0', sizeof o.meta);
+ o.self = obj;
+ o.po_ref = po_idx;
+ o.flags = OF_USED | (with_you ? OF_WITH_YOU : 0);
+ o.pos = c;
+ o.quan = quantity;
+ switch (permobjs[po_idx].poclass)
+ {
+ case POCLASS_WEAPON:
+ case POCLASS_ARMOUR:
+ o.durability = OBJ_MAX_DUR;
+ break;
+ default:
+ break;
+ }
+ objects[obj] = o;
+ if (!(o.flags & OF_WITH_YOU))
+ {
+ lvl.set_obj_at(c, obj);
+ notify_new_obj_at(c, obj);
+ }
+ return obj;
+}
+
+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)
+ {
+ i = asprintf(buf, "%d %s", optr->quan, poptr->plural);
+ }
+ else if (po_is_stackable(optr->po_ref))
+ {
+ i = asprintf(buf, "1 %s", poptr->name);
+ }
+ else if (poptr->flags[0] & POF_DAMAGEABLE)
+ {
+ 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
+ {
+ i = asprintf(buf, "a%s %s", is_vowel(poptr->name[0]) ? "n" : "", poptr->name);
+ }
+ if (i == -1)
+ {
+ // TODO wibble
+ }
+ return;
+}
+
+void sprint_obj_name(char *buf, Obj_handle obj, int len)
+{
+ Obj *optr;
+ Permobj *poptr;
+ optr = obj_snapv(obj);
+ poptr = permobjs + optr->po_ref;
+ if (optr->quan > 1)
+ {
+ snprintf(buf, len, "%d %s", optr->quan, poptr->plural);
+ }
+ else if (po_is_stackable(optr->po_ref))
+ {
+ snprintf(buf, len, "1 %s", poptr->name);
+ }
+ else if (poptr->flags[0] & POF_DAMAGEABLE)
+ {
+ 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
+ {
+ 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;
+ Terrain t = lvl.terrain_at(u.pos);
+ if (terrain_props[t].flags & TFLAG_portal)
+ {
+ notify_drop_blocked();
+ return Cost_none;
+ }
+ optr = obj_snapv(u.inventory[inv_idx]);
+ if (lvl.obj_at(u.pos) == NO_OBJ)
+ {
+ optr->pos = u.pos;
+ lvl.set_obj_at(u.pos, u.inventory[inv_idx]);
+ if (u.weapon == u.inventory[inv_idx])
+ {
+ u.weapon = NO_OBJ;
+ }
+ u.inventory[inv_idx] = NO_OBJ;
+ optr->flags &= ~OF_WITH_YOU;
+ notify_drop_item(lvl.obj_at(u.pos));
+ return Cost_std;
+ }
+ else
+ {
+ notify_drop_blocked();
+ return Cost_none;
+ }
+}
+
+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;
+ int stackable;
+ Obj_handle oh = lvl.obj_at(u.pos);
+ Obj *optr = obj_snapv(oh);
+ Obj *invptr;
+ stackable = po_is_stackable(optr->po_ref);
+ if (stackable)
+ {
+ for (i = 0; i < 19; i++)
+ {
+ invptr = obj_snapv(u.inventory[i]);
+ if (invptr && (invptr->po_ref == optr->po_ref))
+ {
+ invptr->quan += optr->quan;
+ optr->flags &= ~OF_USED;
+ lvl.set_obj_at(u.pos, NO_OBJ);
+ notify_get_item(oh, i);
+ return;
+ }
+ }
+ }
+ for (i = 0; i < 19; i++)
+ {
+ if (u.inventory[i] == NO_OBJ)
+ {
+ break;
+ }
+ }
+ if (i == 19)
+ {
+ notify_pack_full();
+ return;
+ }
+ u.inventory[i] = lvl.obj_at(u.pos);
+ lvl.set_obj_at(u.pos, NO_OBJ);
+ optr->flags |= OF_WITH_YOU;
+ optr->pos = Nowhere;
+ notify_get_item(NO_OBJ, i);
+}
+
+void break_reaction(Obj_handle obj)
+{
+ switch (objects[obj].po_ref)
+ {
+ case PO_SET_OF_RIBBONS:
+ if (u.food < 500)
+ {
+ int shortfall = (u.food < 0) ? 500 : 500 - u.food;
+ int damage = (shortfall + 24) / 25;
+ u.food = (u.food < 0) ? u.food : 0;
+ notify_ribbon_activation(3);
+ damage_u(damage, DEATH_RIBBONS, "");
+ }
+ else
+ {
+ u.food -= 500;
+ objects[obj].durability = 100;
+ notify_ribbon_activation(1);
+ }
+ break;
+
+ case PO_TORMENTORS_LASH:
+ if (u.food < 500)
+ {
+ int shortfall = (u.food < 0) ? 500 : 500 - u.food;
+ int damage = (shortfall + 24) / 25;
+ u.food = (u.food < 0) ? u.food : 0;
+ notify_lash_activation(3);
+ damage_u(damage, DEATH_LASH, "");
+ }
+ else
+ {
+ u.food -= 500;
+ objects[obj].durability = 100;
+ notify_lash_activation(1);
+ }
+ break;
+
+ default:
+ if (permobjs[objects[obj].po_ref].flags[0] & POF_DRESS)
+ {
+ objects[obj].durability = 50 + zero_die(51);
+ objects[obj].po_ref = PO_RAGGED_SHIFT;
+ notify_dress_shredded();
+ recalc_defence();
+ }
+ else
+ {
+ debug_unimplemented_break_reaction(objects[obj].po_ref);
+ }
+ break;
+ }
+}
+
+void damage_obj(Obj_handle obj)
+{
+ /* Only weapons and armour have non-zero durability. */
+ if (objects[obj].durability == 0)
+ {
+ /* Break the object. Weapons and armour don't stack. */
+ consume_obj(obj);
+ }
+ else
+ {
+ objects[obj].durability--;
+ if ((objects[obj].durability == 0) && (permobjs[objects[obj].po_ref].flags[0] & POF_BREAK_REACT))
+ {
+ break_reaction(obj);
+ }
+ }
+}
+
+int evasion_penalty(Obj_handle obj)
+{
+ if (permobjs[objects[obj].po_ref].poclass == POCLASS_ARMOUR)
+ {
+ return permobjs[objects[obj].po_ref].power2;
+ }
+ return 100;
+}
+
+Action_cost magic_ring(void)
+{
+ Obj *optr = obj_snapv(u.ring);
+ switch (optr->po_ref)
+ {
+ case PO_TELEPORT_RING:
+ if (u.food >= 50)
+ {
+ u.food -= 50;
+ notify_telering_activation(1);
+ teleport_u();
+ return Cost_std;
+ }
+ else
+ {
+ notify_telering_activation(0);
+ }
+ return Cost_none;
+ default:
+ if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
+ {
+ debug_unimplemented_activation(optr->po_ref);
+ }
+ else
+ {
+ notify_magic_powerless_ring();
+ }
+ return Cost_none;
+ }
+}
+
+Action_cost emanate_armour(void)
+{
+ Obj *optr = obj_snapv(u.armour);
+ Action_cost cost = Cost_none;
+ switch (optr->po_ref)
+ {
+ case PO_SET_OF_RIBBONS:
+ if (optr->durability < OBJ_MAX_DUR)
+ {
+ if (u.food < 5 * (OBJ_MAX_DUR - optr->durability))
+ {
+ notify_ribbon_activation(0);
+ }
+ else
+ {
+ notify_ribbon_activation(1);
+ u.food -= 5 * (OBJ_MAX_DUR - optr->durability);
+ optr->durability = OBJ_MAX_DUR;
+ cost = Cost_std;
+ }
+ }
+ else
+ {
+ notify_ribbon_activation(2);
+ }
+ break;
+ default:
+ if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
+ {
+ debug_unimplemented_activation(optr->po_ref);
+ }
+ else
+ {
+ notify_emanate_powerless_armour();
+ }
+ break;
+ }
+ return cost;
+}
+
+Action_cost zap_weapon(void)
+{
+ Obj *optr = obj_snapv(u.weapon);
+ int i;
+ for (i = 0; weapon_table[i].pobj != NO_POBJ; ++i)
+ {
+ if (weapon_table[i].pobj == optr->po_ref)
+ {
+ if (weapon_table[i].zap_func != nullptr)
+ {
+ weapon_table[i].zap_func(u.weapon);
+ return Cost_std;
+ }
+ else if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
+ {
+ debug_unimplemented_activation(optr->po_ref);
+ return Cost_none;
+ }
+ break;
+ }
+ }
+ notify_zap_powerless_weapon();
+ return Cost_none;
+}
+
+/*! \brief Unwield the player's currently equipped weapon
+ *
+ * \todo Stickycurse?
+ * \todo Life-essential resistances?
+ */
+Action_cost player_unwield(Noisiness noisy)
+{
+ int saved_weapon = u.weapon;
+ if (u.weapon == NO_OBJ)
+ {
+ debug_unwield_nothing();
+ return Cost_none;
+ }
+ u.weapon = NO_OBJ;
+ recalc_defence();
+ notify_unwield(saved_weapon, Noise_std);
+ return Cost_std;
+}
+
+Pass_fail 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 You_pass;
+}
+
+Action_cost player_wield(Obj_handle obj, Noisiness noisy)
+{
+ Pass_fail rej = role_equip_check(obj, noisy);
+ if (rej == You_pass)
+ {
+ if (u.weapon != NO_OBJ)
+ {
+ player_unwield(Noise_low);
+ }
+ u.weapon = obj;
+ notify_wield_weapon(u.weapon);
+ recalc_defence();
+ return Cost_std;
+ }
+ else
+ {
+ return Cost_none;
+ }
+}
+
+Action_cost wear_armour(Obj_handle obj)
+{
+ if (u.armour != NO_OBJ)
+ {
+ debug_wear_while_wearing();
+ return Cost_none;
+ }
+ if (!(objects[obj].flags & OF_WITH_YOU))
+ {
+ debug_wear_uncarried_armour();
+ return Cost_none;
+ }
+ u.armour = obj;
+ recalc_defence();
+ notify_armour_equip(u.armour);
+ return Cost_std;
+}
+
+Action_cost put_on_ring(Obj_handle obj)
+{
+ if (u.ring != NO_OBJ)
+ {
+ debug_put_on_second_ring();
+ return Cost_none;
+ }
+ if (!(objects[obj].flags & OF_WITH_YOU))
+ {
+ debug_put_on_uncarried_ring();
+ return Cost_none;
+ }
+ u.ring = obj;
+ notify_ring_equip(u.ring);
+ recalc_defence();
+ return Cost_std;
+}
+
+Action_cost remove_ring(void)
+{
+ int saved_ring = u.ring;
+ if (u.ring == NO_OBJ)
+ {
+ debug_remove_no_ring();
+ return Cost_none;
+ }
+ u.ring = NO_OBJ;
+ recalc_defence();
+ notify_ring_unequip(saved_ring);
+ return Cost_std;
+}
+
+Pass_fail unequip_safety_check(uint32_t mask, Noisiness noisy)
+{
+ Terrain t = lvl.terrain_at(u.pos);
+ if (terrain_is_hot(t) &&
+ (!(u.resistances[DT_FIRE] & ~mask)) &&
+ (!(u.flight & ~mask)))
+ {
+ if (noisy != Noise_silent)
+ {
+ notify_hot_blocks_unequip();
+ }
+ return You_fail;
+ }
+ else if (terrain_drowns(t) &&
+ (!(u.passwater & ~mask)) &&
+ (!(u.flight & ~mask)))
+ {
+ if (noisy != Noise_silent)
+ {
+ notify_wet_blocks_unequip();
+ }
+ return You_fail;
+ }
+ else if (terrain_gapes(t) &&
+ (!(u.flight & ~mask)))
+ {
+ if (noisy != Noise_silent)
+ {
+ notify_pit_blocks_unequip();
+ }
+ return You_fail;
+ }
+ return You_pass;
+}
+
+bool corpse_is_blessed(Obj const *optr)
+{
+ return (optr && (optr->po_ref == PO_CORPSE) && (optr->meta[1] == 1));
+}
+
+/* objects.cc */
+// vim:cindent:expandtab
+++ /dev/null
-/* \file objects.cc
- * \brief Object handling
- */
-
-/* Copyright 2005-2013 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 OBJECTS_CC
-#include "victrix-abyssi.hh"
-#include "objects.hh"
-#include "monsters.hh"
-
-#include <string.h>
-
-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)
-{
- Obj *optr = obj_snapv(obj);
- int i;
- for (i = 0; scroll_table[i].read_func != nullptr; ++i)
- {
- if (scroll_table[i].pobj == optr->po_ref)
- {
- scroll_table[i].read_func(obj);
- consume_obj(obj);
- return Cost_std;
- }
- }
- debug_read_non_scroll();
- return Cost_none;
-}
-
-bool consume_obj(Obj_handle obj)
-{
- int i;
- Obj *optr = obj_snapv(obj);
- optr->quan--;
- if (optr->quan == 0)
- {
- optr->flags &= ~OF_USED;
- if (optr->flags & OF_WITH_YOU)
- {
- if (obj == u.armour)
- {
- u.armour = NO_OBJ;
- recalc_defence();
- notify_armour_broke(obj);
- }
- else if (obj == u.weapon)
- {
- u.weapon = NO_OBJ;
- recalc_defence();
- notify_weapon_broke(obj);
- }
- else if (obj == u.ring)
- {
- u.ring = NO_OBJ;
- recalc_defence();
- }
- for (i = 0; i < 19; i++)
- {
- if (u.inventory[i] == obj)
- {
- u.inventory[i] = NO_OBJ;
- break;
- }
- }
- }
- return true;
- }
- return false;
-}
-
-/*! \brief Consume a food */
-Action_cost eat_food(Obj_handle obj)
-{
- Obj *optr = obj_snapv(obj);
- bool ravenous = (u.food < 0);
- if (permobjs[optr->po_ref].poclass != POCLASS_FOOD)
- {
- debug_eat_non_food(obj);
- return Cost_none;
- }
- u.food += permobjs[optr->po_ref].power;
- if (optr->po_ref == PO_DEVIL_SPLEEN)
- {
- notify_ingest_spleen();
- if (zero_die(2))
- {
- gain_body(1);
- }
- else
- {
- gain_agility(1);
- }
- // TODO add more tracking state to this item so we don't have to randomize the fell power choice
- ++u.sympathy[zero_die(TOTAL_FELL_POWERS)];
- }
- else
- {
- notify_eat_food(ravenous);
- }
- consume_obj(obj);
- return Cost_std;
-}
-
-/*! \brief Consume a potion */
-Action_cost quaff_potion(Obj_handle obj)
-{
- Obj *optr = obj_snapv(obj);
- int i;
- for (i = 0; potion_table[i].pobj != NO_POBJ; ++i)
- {
- if (potion_table[i].pobj == optr->po_ref)
- {
- potion_table[i].quaff_func(obj);
- consume_obj(obj);
- return Cost_std;
- }
- }
- debug_quaff_non_potion();
- return Cost_none;
-}
-
-Obj_handle first_free_obj_handle = 1u;
-
-Obj_handle get_first_free_obj(void)
-{
- if (first_free_obj_handle == 0u)
- {
- return NO_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;
- Coord nc = c;
- int panic_count = 200;
- while ((terrain_blocks_items(lvl.terrain_at(nc)) || (lvl.obj_at(c) != NO_OBJ)) && (panic_count > 0))
- {
- do
- {
- delta = random_step();
- nc = c + delta;
- }
- while (terrain_blocks_items(lvl.terrain_at(nc)));
- c = nc;
- --panic_count;
- }
- if (panic_count < 1)
- {
- 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();
- if (obj == NO_OBJ)
- {
- debug_object_pool_exhausted();
- return NO_OBJ;
- }
- if (po_idx == NO_POBJ)
- {
- po_idx = get_random_pobj();
- if (po_idx == NO_POBJ)
- {
- debug_pobj_select_failed();
- return NO_OBJ;
- }
- }
- Obj o;
- memset(o.meta, '\0', sizeof o.meta);
- o.self = obj;
- o.po_ref = po_idx;
- o.flags = OF_USED | (with_you ? OF_WITH_YOU : 0);
- o.pos = c;
- o.quan = quantity;
- switch (permobjs[po_idx].poclass)
- {
- case POCLASS_WEAPON:
- case POCLASS_ARMOUR:
- o.durability = OBJ_MAX_DUR;
- break;
- default:
- break;
- }
- objects[obj] = o;
- if (!(o.flags & OF_WITH_YOU))
- {
- lvl.set_obj_at(c, obj);
- notify_new_obj_at(c, obj);
- }
- return obj;
-}
-
-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)
- {
- i = asprintf(buf, "%d %s", optr->quan, poptr->plural);
- }
- else if (po_is_stackable(optr->po_ref))
- {
- i = asprintf(buf, "1 %s", poptr->name);
- }
- else if (poptr->flags[0] & POF_DAMAGEABLE)
- {
- 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
- {
- i = asprintf(buf, "a%s %s", is_vowel(poptr->name[0]) ? "n" : "", poptr->name);
- }
- if (i == -1)
- {
- // TODO wibble
- }
- return;
-}
-
-void sprint_obj_name(char *buf, Obj_handle obj, int len)
-{
- Obj *optr;
- Permobj *poptr;
- optr = obj_snapv(obj);
- poptr = permobjs + optr->po_ref;
- if (optr->quan > 1)
- {
- snprintf(buf, len, "%d %s", optr->quan, poptr->plural);
- }
- else if (po_is_stackable(optr->po_ref))
- {
- snprintf(buf, len, "1 %s", poptr->name);
- }
- else if (poptr->flags[0] & POF_DAMAGEABLE)
- {
- 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
- {
- 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;
- Terrain t = lvl.terrain_at(u.pos);
- if (terrain_props[t].flags & TFLAG_portal)
- {
- notify_drop_blocked();
- return Cost_none;
- }
- optr = obj_snapv(u.inventory[inv_idx]);
- if (lvl.obj_at(u.pos) == NO_OBJ)
- {
- optr->pos = u.pos;
- lvl.set_obj_at(u.pos, u.inventory[inv_idx]);
- if (u.weapon == u.inventory[inv_idx])
- {
- u.weapon = NO_OBJ;
- }
- u.inventory[inv_idx] = NO_OBJ;
- optr->flags &= ~OF_WITH_YOU;
- notify_drop_item(lvl.obj_at(u.pos));
- return Cost_std;
- }
- else
- {
- notify_drop_blocked();
- return Cost_none;
- }
-}
-
-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;
- int stackable;
- Obj_handle oh = lvl.obj_at(u.pos);
- Obj *optr = obj_snapv(oh);
- Obj *invptr;
- stackable = po_is_stackable(optr->po_ref);
- if (stackable)
- {
- for (i = 0; i < 19; i++)
- {
- invptr = obj_snapv(u.inventory[i]);
- if (invptr && (invptr->po_ref == optr->po_ref))
- {
- invptr->quan += optr->quan;
- optr->flags &= ~OF_USED;
- lvl.set_obj_at(u.pos, NO_OBJ);
- notify_get_item(oh, i);
- return;
- }
- }
- }
- for (i = 0; i < 19; i++)
- {
- if (u.inventory[i] == NO_OBJ)
- {
- break;
- }
- }
- if (i == 19)
- {
- notify_pack_full();
- return;
- }
- u.inventory[i] = lvl.obj_at(u.pos);
- lvl.set_obj_at(u.pos, NO_OBJ);
- optr->flags |= OF_WITH_YOU;
- optr->pos = Nowhere;
- notify_get_item(NO_OBJ, i);
-}
-
-void break_reaction(Obj_handle obj)
-{
- switch (objects[obj].po_ref)
- {
- case PO_SET_OF_RIBBONS:
- if (u.food < 500)
- {
- int shortfall = (u.food < 0) ? 500 : 500 - u.food;
- int damage = (shortfall + 24) / 25;
- u.food = (u.food < 0) ? u.food : 0;
- notify_ribbon_activation(3);
- damage_u(damage, DEATH_RIBBONS, "");
- }
- else
- {
- u.food -= 500;
- objects[obj].durability = 100;
- notify_ribbon_activation(1);
- }
- break;
-
- case PO_TORMENTORS_LASH:
- if (u.food < 500)
- {
- int shortfall = (u.food < 0) ? 500 : 500 - u.food;
- int damage = (shortfall + 24) / 25;
- u.food = (u.food < 0) ? u.food : 0;
- notify_lash_activation(3);
- damage_u(damage, DEATH_LASH, "");
- }
- else
- {
- u.food -= 500;
- objects[obj].durability = 100;
- notify_lash_activation(1);
- }
- break;
-
- default:
- if (permobjs[objects[obj].po_ref].flags[0] & POF_DRESS)
- {
- objects[obj].durability = 50 + zero_die(51);
- objects[obj].po_ref = PO_RAGGED_SHIFT;
- notify_dress_shredded();
- recalc_defence();
- }
- else
- {
- debug_unimplemented_break_reaction(objects[obj].po_ref);
- }
- break;
- }
-}
-
-void damage_obj(Obj_handle obj)
-{
- /* Only weapons and armour have non-zero durability. */
- if (objects[obj].durability == 0)
- {
- /* Break the object. Weapons and armour don't stack. */
- consume_obj(obj);
- }
- else
- {
- objects[obj].durability--;
- if ((objects[obj].durability == 0) && (permobjs[objects[obj].po_ref].flags[0] & POF_BREAK_REACT))
- {
- break_reaction(obj);
- }
- }
-}
-
-int evasion_penalty(Obj_handle obj)
-{
- if (permobjs[objects[obj].po_ref].poclass == POCLASS_ARMOUR)
- {
- return permobjs[objects[obj].po_ref].power2;
- }
- return 100;
-}
-
-Action_cost magic_ring(void)
-{
- Obj *optr = obj_snapv(u.ring);
- switch (optr->po_ref)
- {
- case PO_TELEPORT_RING:
- if (u.food >= 50)
- {
- u.food -= 50;
- notify_telering_activation(1);
- teleport_u();
- return Cost_std;
- }
- else
- {
- notify_telering_activation(0);
- }
- return Cost_none;
- default:
- if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
- {
- debug_unimplemented_activation(optr->po_ref);
- }
- else
- {
- notify_magic_powerless_ring();
- }
- return Cost_none;
- }
-}
-
-Action_cost emanate_armour(void)
-{
- Obj *optr = obj_snapv(u.armour);
- Action_cost cost = Cost_none;
- switch (optr->po_ref)
- {
- case PO_SET_OF_RIBBONS:
- if (optr->durability < OBJ_MAX_DUR)
- {
- if (u.food < 5 * (OBJ_MAX_DUR - optr->durability))
- {
- notify_ribbon_activation(0);
- }
- else
- {
- notify_ribbon_activation(1);
- u.food -= 5 * (OBJ_MAX_DUR - optr->durability);
- optr->durability = OBJ_MAX_DUR;
- cost = Cost_std;
- }
- }
- else
- {
- notify_ribbon_activation(2);
- }
- break;
- default:
- if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
- {
- debug_unimplemented_activation(optr->po_ref);
- }
- else
- {
- notify_emanate_powerless_armour();
- }
- break;
- }
- return cost;
-}
-
-Action_cost zap_weapon(void)
-{
- Obj *optr = obj_snapv(u.weapon);
- int i;
- for (i = 0; weapon_table[i].pobj != NO_POBJ; ++i)
- {
- if (weapon_table[i].pobj == optr->po_ref)
- {
- if (weapon_table[i].zap_func != nullptr)
- {
- weapon_table[i].zap_func(u.weapon);
- return Cost_std;
- }
- else if (permobjs[optr->po_ref].flags[0] & POF_ACTIVATABLE)
- {
- debug_unimplemented_activation(optr->po_ref);
- return Cost_none;
- }
- break;
- }
- }
- notify_zap_powerless_weapon();
- return Cost_none;
-}
-
-/*! \brief Unwield the player's currently equipped weapon
- *
- * \todo Stickycurse?
- * \todo Life-essential resistances?
- */
-Action_cost player_unwield(Noisiness noisy)
-{
- int saved_weapon = u.weapon;
- if (u.weapon == NO_OBJ)
- {
- debug_unwield_nothing();
- return Cost_none;
- }
- u.weapon = NO_OBJ;
- recalc_defence();
- notify_unwield(saved_weapon, Noise_std);
- return Cost_std;
-}
-
-Pass_fail 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 You_pass;
-}
-
-Action_cost player_wield(Obj_handle obj, Noisiness noisy)
-{
- Pass_fail rej = role_equip_check(obj, noisy);
- if (rej == You_pass)
- {
- if (u.weapon != NO_OBJ)
- {
- player_unwield(Noise_low);
- }
- u.weapon = obj;
- notify_wield_weapon(u.weapon);
- recalc_defence();
- return Cost_std;
- }
- else
- {
- return Cost_none;
- }
-}
-
-Action_cost wear_armour(Obj_handle obj)
-{
- if (u.armour != NO_OBJ)
- {
- debug_wear_while_wearing();
- return Cost_none;
- }
- if (!(objects[obj].flags & OF_WITH_YOU))
- {
- debug_wear_uncarried_armour();
- return Cost_none;
- }
- u.armour = obj;
- recalc_defence();
- notify_armour_equip(u.armour);
- return Cost_std;
-}
-
-Action_cost put_on_ring(Obj_handle obj)
-{
- if (u.ring != NO_OBJ)
- {
- debug_put_on_second_ring();
- return Cost_none;
- }
- if (!(objects[obj].flags & OF_WITH_YOU))
- {
- debug_put_on_uncarried_ring();
- return Cost_none;
- }
- u.ring = obj;
- notify_ring_equip(u.ring);
- recalc_defence();
- return Cost_std;
-}
-
-Action_cost remove_ring(void)
-{
- int saved_ring = u.ring;
- if (u.ring == NO_OBJ)
- {
- debug_remove_no_ring();
- return Cost_none;
- }
- u.ring = NO_OBJ;
- recalc_defence();
- notify_ring_unequip(saved_ring);
- return Cost_std;
-}
-
-Pass_fail unequip_safety_check(uint32_t mask, Noisiness noisy)
-{
- Terrain t = lvl.terrain_at(u.pos);
- if (terrain_is_hot(t) &&
- (!(u.resistances[DT_FIRE] & ~mask)) &&
- (!(u.flight & ~mask)))
- {
- if (noisy != Noise_silent)
- {
- notify_hot_blocks_unequip();
- }
- return You_fail;
- }
- else if (terrain_drowns(t) &&
- (!(u.passwater & ~mask)) &&
- (!(u.flight & ~mask)))
- {
- if (noisy != Noise_silent)
- {
- notify_wet_blocks_unequip();
- }
- return You_fail;
- }
- else if (terrain_gapes(t) &&
- (!(u.flight & ~mask)))
- {
- if (noisy != Noise_silent)
- {
- notify_pit_blocks_unequip();
- }
- return You_fail;
- }
- return You_pass;
-}
-
-bool corpse_is_blessed(Obj const *optr)
-{
- return (optr && (optr->po_ref == PO_CORPSE) && (optr->meta[1] == 1));
-}
-
-/* objects.cc */
-// vim:cindent:expandtab