Finished the merge!
authorMartin Read <mpread@chiark.greenend.org.uk>
Sun, 16 Mar 2014 21:06:43 +0000 (21:06 +0000)
committerMartin Read <mpread@chiark.greenend.org.uk>
Sun, 16 Mar 2014 21:06:43 +0000 (21:06 +0000)
Victrix Abyssi now incorporates approximately all of the engineering
improvements I made over the course of writing Obumbrata et Velata.

15 files changed:
MANIFEST
Makefile
cave.cc
core.hh
default.permobjs
default.permons
display-nc.cc
dungeon.cc
log.cc
map.cc
mapgen.hh
mon1.cc [new file with mode: 0644]
monsters.cc [deleted file]
obj1.cc [new file with mode: 0644]
objects.cc [deleted file]

index dadebd6..58af5bd 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -7,13 +7,16 @@ man/victrix-abyssi.6
 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
@@ -21,13 +24,15 @@ main.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
@@ -37,7 +42,9 @@ pmon_comp
 pobj_comp
 rng.cc
 rng.hh
+role.cc
 shrine.cc
+skills.cc
 sorcery.cc
 sorcery.hh
 u.cc
index ca0b39c..4f04799 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,13 +9,13 @@ vpath %.o .
 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
@@ -111,7 +111,7 @@ main.o: $(srcdir)/main.cc $(srcdir)/combat.hh $(srcdir)/$(GAME).hh $(srcdir)/mon
 
 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
 
@@ -125,7 +125,7 @@ permons.o: ./permons.cc $(srcdir)/core.hh $(srcdir)/permon.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
 
diff --git a/cave.cc b/cave.cc
index c3c12df..c93e450 100644 (file)
--- a/cave.cc
+++ b/cave.cc
@@ -67,7 +67,7 @@ void build_level_cave(Level *l)
     {
     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);
     }
@@ -98,7 +98,7 @@ void build_level_cave(Level *l)
         {
             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);
         }
@@ -123,7 +123,7 @@ void build_level_intrusions(Level *l)
     {
         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);
     }
@@ -145,7 +145,7 @@ void build_level_pits(Level *l, Terrain pit_type)
     {
         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);
     }
@@ -154,7 +154,7 @@ void build_level_pits(Level *l, Terrain pit_type)
     {
         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);
     }
@@ -163,7 +163,7 @@ void build_level_pits(Level *l, Terrain pit_type)
     {
     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);
     }
@@ -172,7 +172,7 @@ void build_level_pits(Level *l, Terrain pit_type)
     {
     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);
     }
@@ -182,7 +182,7 @@ void build_level_pits(Level *l, Terrain pit_type)
     {
     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);
     }
diff --git a/core.hh b/core.hh
index cecf024..a9ce662 100644 (file)
--- a/core.hh
+++ b/core.hh
@@ -252,22 +252,22 @@ enum Role_id {
 #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;
index 27e441b..9dc0ef1 100644 (file)
@@ -34,10 +34,11 @@ POWER2 0
 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 ")"
@@ -47,6 +48,37 @@ POWER2 0
 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
@@ -60,19 +92,21 @@ POWER2 0
 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
@@ -88,6 +122,8 @@ NOTIFY_EQUIP
 DAMAGEABLE
 BREAK_REACT
 MELEE_WEAPON
+DEMONIC
+POLEARM
 
 WEAPON plague scythe
 PLURAL plague sycthes
@@ -101,6 +137,8 @@ POWER2 0
 DEPTH 30
 NOTIFY_EQUIP
 MELEE_WEAPON
+DEMONIC
+POLEARM
 
 WEAPON tormentor's lash
 PLURAL tormentor's lashes
@@ -116,6 +154,8 @@ NOTIFY_EQUIP
 DAMAGEABLE
 BREAK_REACT
 MELEE_WEAPON
+DEMONIC
+WHIP
 
 WEAPON death staff
 PLURAL death staves
@@ -129,6 +169,7 @@ POWER2 0
 DEPTH 30
 NOTIFY_EQUIP
 MELEE_WEAPON
+BLUDGEON
 
 WEAPON staff of fire
 PLURAL staves of fire
@@ -143,6 +184,7 @@ DEPTH 20
 DAMAGEABLE
 ACTIVATABLE
 MELEE_WEAPON
+BLUDGEON
 
 WEAPON bow
 PLURAL bows
@@ -156,6 +198,7 @@ POWER2 0
 DEPTH 1
 DAMAGEABLE
 RANGED_WEAPON
+
 WEAPON crossbow
 PLURAL crossbows
 DESC A crossbow.
@@ -277,6 +320,7 @@ POWER 3
 POWER2 10
 DEPTH 1
 DAMAGEABLE
+LEATHERY
 
 ARMOUR chainmail
 PLURAL suits of chainmail
@@ -325,6 +369,7 @@ POWER 2
 POWER2 5
 DEPTH 1
 DAMAGEABLE
+ROBE
 
 ARMOUR robe of swiftness
 PLURAL robes of swiftness
@@ -337,6 +382,8 @@ POWER 8
 POWER2 0
 DEPTH 8
 DAMAGEABLE
+SPEED
+ROBE
 
 ARMOUR robe of shadows
 PLURAL robes of shadows
@@ -349,6 +396,7 @@ POWER 14
 POWER2 -15
 DEPTH 18
 DAMAGEABLE
+ROBE
 
 ARMOUR dragonhide armour
 PLURAL suits of dragonhide armour
@@ -361,6 +409,8 @@ POWER 12
 POWER2 10
 DEPTH 21
 DAMAGEABLE
+RES_FIRE
+LEATHERY
 
 ARMOUR meteoric plate armour
 PLURAL suits of meteoric plate armour
@@ -373,6 +423,7 @@ POWER 18
 POWER2 40
 DEPTH 27
 DAMAGEABLE
+RES_FIRE
 
 ARMOUR sacred chainmail
 PLURAL suits of sacred chainmail
@@ -408,7 +459,7 @@ UTF8 "["
 COLOUR green
 POWER 3
 POWER2 10
-DEPTH 1
+DEPTH 3
 DRESS
 DAMAGEABLE
 BREAK_REACT
@@ -440,6 +491,9 @@ DEPTH 30
 NOTIFY_EQUIP
 DAMAGEABLE
 BREAK_REACT
+RES_POISON
+DEMONIC
+ROBE
 
 ARMOUR lich's robe
 PLURAL lich's robes
@@ -452,6 +506,8 @@ POWER 15
 POWER2 0
 DEPTH 30
 NOTIFY_EQUIP
+RES_NECRO
+ROBE
 
 ARMOUR infernite armour
 PLURAL suits of infernite armour
@@ -464,6 +520,8 @@ POWER 25
 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_.
@@ -507,6 +565,8 @@ COLOUR l_grey
 POWER 0
 POWER2 0
 DEPTH 1
+RES_FIRE
+DMG_FIRE
 
 RING vampire ring
 PLURAL vampire rings
@@ -518,6 +578,8 @@ COLOUR l_grey
 POWER 0
 POWER2 0
 DEPTH 12
+RES_NECRO
+DMG_NECRO
 
 RING frost ring
 PLURAL frost rings
@@ -529,6 +591,9 @@ COLOUR l_grey
 POWER 0
 POWER2 0
 DEPTH 1
+RES_COLD
+DMG_COLD
+PASS_WATER
 
 RING teleport ring
 PLURAL teleport rings
@@ -551,6 +616,8 @@ COLOUR l_grey
 POWER 0
 POWER2 0
 DEPTH 15
+RES_POISON
+DEMONIC
 
 RING protection ring
 PLURAL protection rings
@@ -562,10 +629,11 @@ COLOUR l_grey
 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 "="
@@ -582,7 +650,7 @@ RARITY 75
 ASCII '%'
 UTF8 "%"
 COLOUR brown
-POWER 0
+POWER 1500
 POWER2 0
 DEPTH 1
 STACKABLE
@@ -594,7 +662,7 @@ RARITY 75
 ASCII '%'
 UTF8 "%"
 COLOUR yellow
-POWER 0
+POWER 1500
 POWER2 0
 DEPTH 1
 STACKABLE
@@ -606,7 +674,7 @@ RARITY 85
 ASCII '%'
 UTF8 "%"
 COLOUR white
-POWER 0
+POWER 1500
 POWER2 0
 DEPTH 1
 STACKABLE
@@ -622,6 +690,7 @@ POWER 0
 POWER2 0
 DEPTH 1
 STACKABLE
+DEMONIC
 
 CARRION corpse
 PLURAL corpses
index 3fc37cd..dd04484 100644 (file)
 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+monster adventurer
+ascii '@'
+utf8 "@"
+desc A typical morally stunted specimen of the dungeoneering class.
+rarity 100
+power 1
+hp 1
+mtohit 0
+mdam 1
+defence 1
+exp 0
+speed 1
+STUPID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
+
 monster newt
 ascii 'n'
 utf8 "n"
@@ -37,6 +54,9 @@ speed 0
 STUPID
 SMALL
 QUADRUPED
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # rodents
 monster rat
@@ -55,6 +75,9 @@ speed 2
 STUPID
 SMALL
 QUADRUPED
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # canids
 monster wolf
@@ -72,6 +95,9 @@ defence 6
 exp 15
 speed 2
 QUADRUPED
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # Serpents
 monster snake
@@ -89,6 +115,9 @@ experience 40
 speed 2
 STUPID
 SERPENTINE
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # thugs
 monster thug
@@ -105,6 +134,9 @@ defence 4
 experience 5
 speed 1
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 monster goon
 desc Really good at pushing people's faces through the backs of their heads.
@@ -120,6 +152,9 @@ defence 8
 experience 10
 speed 1
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # hunters
 monster hunter
@@ -140,6 +175,9 @@ experience 50
 speed 1
 ARCHER
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # fighters
 monster duellist
@@ -157,6 +195,9 @@ experience 130
 speed 1
 SMART
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 monster warlord
 desc A truly exceptional warrior, strong of arm and fleet of foot.
@@ -173,6 +214,28 @@ experience 400
 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
@@ -189,6 +252,9 @@ defence 3
 experience 3
 speed 1
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # elves
 monster bad elf
@@ -207,6 +273,9 @@ experience 15
 speed 2
 SMART
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # Trolls
 monster troll
@@ -225,6 +294,9 @@ speed 1
 STUPID
 HUMANOID
 HUGE
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # Huge humanoids
 monster giant
@@ -243,6 +315,9 @@ speed 1
 STUPID
 HUMANOID
 HUGE
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 monster giant jarl
 desc A rarity among giants, this one has used its great strength and unusually sharp wits to persuade its dimmer brethren to obey.
@@ -259,6 +334,9 @@ experience 1000
 speed 1
 HUMANOID
 HUGE
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # wizards
 monster wizard
@@ -281,6 +359,9 @@ speed 1
 SMART
 MAGICIAN
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 monster archmage
 plural archmagi
@@ -304,6 +385,9 @@ SMART
 MAGICIAN
 RESIST_ELEC
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
+LEAVES_CORPSE
 
 # zombie
 monster zombie
@@ -325,6 +409,7 @@ RESIST_COLD
 RESIST_POIS
 RESIST_NECR
 HUMANOID
+MADE_OF_MEAT
 
 # Wraiths
 monster wraith
@@ -374,6 +459,7 @@ RESIST_POIS
 RESIST_NECR
 MAGICIAN
 HUMANOID
+MADE_OF_BONE
 
 monster master lich
 plural master liches
@@ -400,6 +486,7 @@ RESIST_POIS
 RESIST_NECR
 MAGICIAN
 HUMANOID
+MADE_OF_BONE
 
 # Vampires
 monster vampire
@@ -421,6 +508,8 @@ RESIST_COLD
 RESIST_POIS
 RESIST_NECR
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
 
 # 4th tier demons
 monster fire imp
@@ -440,6 +529,7 @@ DEMONIC
 RESIST_FIRE
 FLYING
 HUMANOID
+MADE_OF_MEAT
 
 # 3rd tier demons
 monster demon
@@ -459,6 +549,7 @@ SMART
 DEMONIC
 RESIST_FIRE
 HUMANOID
+MADE_OF_MEAT
 
 # 2nd tier demons
 monster defiler
@@ -479,6 +570,7 @@ DEMONIC
 MAGICIAN
 RESIST_POIS
 AMORPHOUS
+MADE_OF_GOO
 
 # 1st tier demons
 monster putrid emissary
@@ -498,6 +590,9 @@ SMART
 DEMONIC
 RESIST_POIS
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_PUS
+BURSTS_ON_DEATH
 
 monster tormentor
 desc The faceless violet-skinned figure before you wears a strangely cut garment of pale, supple leather which would be far beyond the limits of decency if there were any indecent parts to reveal. With one hand it beckons to you, while from the other it trails a long whip of the same leather as its garment.
@@ -516,6 +611,7 @@ SMART
 DEMONIC
 RESIST_POIS
 HUMANOID
+MADE_OF_MEAT
 
 monster iron lord
 desc This loyal and unwavering servant of Verant, Great Smith of the Hells, bears arms and armour from its master's forges. Fear the iron lord!
@@ -537,6 +633,8 @@ RESIST_FIRE
 RESIST_COLD
 RESIST_SLAM
 HUMANOID
+MADE_OF_MEAT
+CONTAINS_BLOOD
 
 monster centaur
 desc A strange magical hybrid of horse and man.
@@ -552,11 +650,14 @@ defence 10
 experience 50
 speed 2
 CENTAUROID
+MADE_OF_MEAT
+LEAVES_CORPSE
+CONTAINS_BLOOD
 
 monster ice monster
 desc A ponderous, shambling half-humanoid figure of ice and snow.
-ascii 'I'
-utf8 "I"
+ascii 'E'
+utf8 "E"
 colour white
 rarity 50
 power 6
@@ -573,6 +674,22 @@ speed 0
 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.
@@ -595,6 +712,9 @@ RESIST_FIRE
 ARCHER
 QUADRUPED
 HUGE
+MADE_OF_MEAT
+LEAVES_CORPSE
+CONTAINS_BLOOD
 
 monster moondrake
 desc A bat-winged serpent with eyes that shimmer like quicksilver and a breath that chills the living to the bone.
@@ -619,3 +739,6 @@ RESIST_NECR
 FLYING
 ARCHER
 SERPENTINE
+MADE_OF_MEAT
+LEAVES_CORPSE
+CONTAINS_BLOOD
index 83c401b..18a4745 100644 (file)
@@ -1732,12 +1732,12 @@ static void run_main_menu(void)
                    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);
index e99e9f8..4165087 100644 (file)
@@ -104,7 +104,7 @@ static void excavate_pillar_room(Level *l, Coord inner_tl, Coord inner_br, uint3
     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?
diff --git a/log.cc b/log.cc
index 708746d..189ae72 100644 (file)
--- a/log.cc
+++ b/log.cc
@@ -56,7 +56,7 @@ void wrapped_system(char const *cmd)
     int i = system(cmd);
     if (i != 0)
     {
-        throw Obumb_sysexcep(errno);
+        throw Game_sysexcep(errno);
     }
 }
 
@@ -68,15 +68,15 @@ void wrapped_fread(void *buf, size_t size, size_t nmemb, FILE *fp)
     {
         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);
         }
     }
 }
@@ -220,7 +220,7 @@ static void check_monhandle(Mon_handle mon)
 {
     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");
     }
 }
 
@@ -228,7 +228,7 @@ static void check_pmref(int pm)
 {
     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");
     }
 }
 
@@ -251,7 +251,7 @@ static void check_poref(int po)
 {
     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");
     }
 }
 
@@ -260,7 +260,7 @@ static void check_objhandle(Obj_handle obj)
 {
     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");
     }
 }
 
@@ -524,7 +524,7 @@ void deserialize_level(FILE *fp, Level *l)
         }
         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]);
@@ -589,7 +589,7 @@ void save_game(void)
         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);
@@ -644,9 +644,9 @@ static void rebuild_mapobjs(void)
 
 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");
     }
 }
 
@@ -654,7 +654,7 @@ static void check_minvers(uint32_t minvers)
 {
     if (minvers != MINVERS)
     {
-        throw Obumb_dataexcep("Minor-version mismatch");
+        throw Game_dataexcep("Minor-version mismatch");
     }
 }
 
@@ -662,7 +662,7 @@ static void check_majvers(uint32_t majvers)
 {
     if (majvers != MAJVERS)
     {
-        throw Obumb_dataexcep("Major-version mismatch");
+        throw Game_dataexcep("Major-version mismatch");
     }
 }
 
@@ -680,12 +680,12 @@ void load_game(void)
         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);
@@ -698,12 +698,12 @@ void load_game(void)
         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);
@@ -753,7 +753,7 @@ void load_game(void)
         }
         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 */
@@ -761,7 +761,7 @@ void load_game(void)
         game_cleanup();
         throw;
     }
-    catch (Obumb_sysexcep e)
+    catch (Game_sysexcep e)
     {
         if (fp)
         {
diff --git a/map.cc b/map.cc
index d99a107..e7764c1 100644 (file)
--- a/map.cc
+++ b/map.cc
@@ -343,7 +343,7 @@ Coord run_random_walk(Level *l, Coord oc, rwalk_mod_funcptr func,
     }
     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;
 }
@@ -400,7 +400,7 @@ Coord run_random_walk_unbounded(Level *l, Coord oc, rwalk_mod_funcptr func,
     }
     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;
 }
index 8d14745..513b687 100644 (file)
--- a/mapgen.hh
+++ b/mapgen.hh
@@ -65,11 +65,11 @@ Coord run_random_walk(Level *l, Coord oc, rwalk_mod_funcptr func,
 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
diff --git a/mon1.cc b/mon1.cc
new file mode 100644 (file)
index 0000000..30f0a46
--- /dev/null
+++ b/mon1.cc
@@ -0,0 +1,617 @@
+/*! \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
diff --git a/monsters.cc b/monsters.cc
deleted file mode 100644 (file)
index 30f0a46..0000000
+++ /dev/null
@@ -1,617 +0,0 @@
-/*! \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
diff --git a/obj1.cc b/obj1.cc
new file mode 100644 (file)
index 0000000..b92cf3f
--- /dev/null
+++ b/obj1.cc
@@ -0,0 +1,851 @@
+/* \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
diff --git a/objects.cc b/objects.cc
deleted file mode 100644 (file)
index b92cf3f..0000000
+++ /dev/null
@@ -1,851 +0,0 @@
-/* \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