Merge branch 'panlord-colour'
authorSteve Melenchuk <smelenchuk@gmail.com>
Sun, 30 Nov 2014 05:56:06 +0000 (22:56 -0700)
committerSteve Melenchuk <smelenchuk@gmail.com>
Sun, 30 Nov 2014 05:56:06 +0000 (22:56 -0700)
360 files changed:
.travis/build.pl
crawl-ref/CREDITS.txt
crawl-ref/docs/changelog.txt
crawl-ref/docs/develop/levels/syntax.txt
crawl-ref/docs/options_guide.txt
crawl-ref/settings/init.txt
crawl-ref/settings/safe_move_shift.txt [new file with mode: 0644]
crawl-ref/source/MSVC/crawl.vcxproj
crawl-ref/source/MSVC/crawl.vcxproj.filters
crawl-ref/source/Makefile
crawl-ref/source/Makefile.obj
crawl-ref/source/ability.cc
crawl-ref/source/abyss.cc
crawl-ref/source/acquire.cc
crawl-ref/source/acquire.h
crawl-ref/source/actor.cc
crawl-ref/source/actor.h
crawl-ref/source/areas.cc
crawl-ref/source/arena.cc
crawl-ref/source/art-func.h
crawl-ref/source/attack.cc
crawl-ref/source/attack.h
crawl-ref/source/attitude-change.cc
crawl-ref/source/beam.cc
crawl-ref/source/beam.h
crawl-ref/source/behold.cc
crawl-ref/source/bloodspatter.h
crawl-ref/source/book-data.h
crawl-ref/source/branch-data.h
crawl-ref/source/branch.h
crawl-ref/source/butcher.cc
crawl-ref/source/chardump.cc
crawl-ref/source/chardump.h
crawl-ref/source/cio.cc
crawl-ref/source/cio.h
crawl-ref/source/cloud.cc
crawl-ref/source/clua.cc
crawl-ref/source/clua.h
crawl-ref/source/cluautil.cc
crawl-ref/source/cluautil.h
crawl-ref/source/cmd-keys.h
crawl-ref/source/colour.cc
crawl-ref/source/colour.h
crawl-ref/source/command.cc
crawl-ref/source/crash.cc
crawl-ref/source/ctest.cc
crawl-ref/source/dat/database/godspeak.txt
crawl-ref/source/dat/database/monspell.txt
crawl-ref/source/dat/defaults/runrest_messages.txt
crawl-ref/source/dat/des/altar/lugonu_bribe.des
crawl-ref/source/dat/des/branches/depths_encompass.des
crawl-ref/source/dat/des/branches/geh.des
crawl-ref/source/dat/des/branches/zot.des
crawl-ref/source/dat/des/builder/layout.des
crawl-ref/source/dat/des/builder/layout_cc.des [new file with mode: 0644]
crawl-ref/source/dat/des/portals/wizlab.des
crawl-ref/source/dat/des/variable/float.des
crawl-ref/source/dat/des/variable/mini_monsters.des
crawl-ref/source/dat/descript/commands.txt
crawl-ref/source/dat/descript/gods.txt
crawl-ref/source/dat/descript/items.txt
crawl-ref/source/dat/descript/monsters.txt
crawl-ref/source/dat/descript/quotes.txt
crawl-ref/source/dat/descript/spells.txt
crawl-ref/source/dat/dlua/ziggurat.lua
crawl-ref/source/database.cc
crawl-ref/source/database.h
crawl-ref/source/dbg-asrt.cc
crawl-ref/source/dbg-scan.cc
crawl-ref/source/decks.cc
crawl-ref/source/delay.cc
crawl-ref/source/delay.h
crawl-ref/source/describe-god.cc
crawl-ref/source/describe-spells.cc [new file with mode: 0644]
crawl-ref/source/describe-spells.h [new file with mode: 0644]
crawl-ref/source/describe.cc
crawl-ref/source/describe.h
crawl-ref/source/dgn-labyrinth.cc
crawl-ref/source/dgn-layouts.cc
crawl-ref/source/dgn-shoals.cc
crawl-ref/source/dgn-swamp.cc
crawl-ref/source/dgnevent.cc
crawl-ref/source/directn.cc
crawl-ref/source/directn.h
crawl-ref/source/dlua.cc
crawl-ref/source/dungeon.cc
crawl-ref/source/dungeon.h
crawl-ref/source/duration-data.h
crawl-ref/source/effects.cc
crawl-ref/source/effects.h
crawl-ref/source/end.h
crawl-ref/source/english.cc
crawl-ref/source/english.h
crawl-ref/source/enum.h
crawl-ref/source/evoke.cc
crawl-ref/source/externs.h
crawl-ref/source/fearmonger.cc
crawl-ref/source/fight.cc
crawl-ref/source/fight.h
crawl-ref/source/files.cc
crawl-ref/source/files.h
crawl-ref/source/fineff.cc
crawl-ref/source/flood_find.h
crawl-ref/source/fontwrapper-ft.cc
crawl-ref/source/food.cc
crawl-ref/source/format.cc
crawl-ref/source/format.h
crawl-ref/source/ghost.cc
crawl-ref/source/ghost.h
crawl-ref/source/glwrapper-ogl.cc
crawl-ref/source/godabil.cc
crawl-ref/source/godabil.h
crawl-ref/source/godblessing.cc
crawl-ref/source/godblessing.h
crawl-ref/source/godconduct.cc
crawl-ref/source/godconduct.h
crawl-ref/source/godpassive.h
crawl-ref/source/godprayer.cc
crawl-ref/source/godwrath.cc
crawl-ref/source/hiscores.cc
crawl-ref/source/hiscores.h
crawl-ref/source/initfile.cc
crawl-ref/source/invent.cc
crawl-ref/source/invent.h
crawl-ref/source/item_use.cc
crawl-ref/source/item_use.h
crawl-ref/source/itemname.cc
crawl-ref/source/itemname.h
crawl-ref/source/itemprop.h
crawl-ref/source/items.cc
crawl-ref/source/items.h
crawl-ref/source/json.cc
crawl-ref/source/json.h
crawl-ref/source/kills.cc
crawl-ref/source/l_colour.cc
crawl-ref/source/l_crawl.cc
crawl-ref/source/l_debug.cc
crawl-ref/source/l_dgn.cc
crawl-ref/source/l_dgnbld.cc
crawl-ref/source/l_dgnevt.cc
crawl-ref/source/l_dgngrd.cc
crawl-ref/source/l_dgnit.cc
crawl-ref/source/l_dgnlvl.cc
crawl-ref/source/l_dgnmon.cc
crawl-ref/source/l_dgntil.cc
crawl-ref/source/l_feat.cc
crawl-ref/source/l_file.cc
crawl-ref/source/l_food.cc
crawl-ref/source/l_item.cc
crawl-ref/source/l_los.cc
crawl-ref/source/l_mapgrd.cc
crawl-ref/source/l_mapmrk.cc
crawl-ref/source/l_moninf.cc
crawl-ref/source/l_mons.cc
crawl-ref/source/l_option.cc
crawl-ref/source/l_spells.cc
crawl-ref/source/l_subvault.cc
crawl-ref/source/l_travel.cc
crawl-ref/source/l_view.cc
crawl-ref/source/l_you.cc
crawl-ref/source/libutil.cc
crawl-ref/source/libw32c.cc
crawl-ref/source/los.cc
crawl-ref/source/losglobal.cc
crawl-ref/source/losparam.cc
crawl-ref/source/luaterp.cc
crawl-ref/source/macro.cc
crawl-ref/source/macro.h
crawl-ref/source/main.cc
crawl-ref/source/makeitem.cc
crawl-ref/source/mapdef.cc
crawl-ref/source/mapdef.h
crawl-ref/source/mapmark.cc
crawl-ref/source/mapmark.h
crawl-ref/source/maps.cc
crawl-ref/source/matrix.h
crawl-ref/source/melee_attack.cc
crawl-ref/source/menu.cc
crawl-ref/source/menu.h
crawl-ref/source/message.cc
crawl-ref/source/message.h
crawl-ref/source/mgen_data.h
crawl-ref/source/misc.cc
crawl-ref/source/misc.h
crawl-ref/source/mon-abil.cc
crawl-ref/source/mon-act.cc
crawl-ref/source/mon-behv.cc
crawl-ref/source/mon-cast.cc
crawl-ref/source/mon-cast.h
crawl-ref/source/mon-clone.cc
crawl-ref/source/mon-clone.h
crawl-ref/source/mon-data.h
crawl-ref/source/mon-death.cc
crawl-ref/source/mon-ench.cc
crawl-ref/source/mon-grow.cc
crawl-ref/source/mon-info.cc
crawl-ref/source/mon-info.h
crawl-ref/source/mon-movetarget.cc
crawl-ref/source/mon-pathfind.cc
crawl-ref/source/mon-pick-data.h
crawl-ref/source/mon-place.cc
crawl-ref/source/mon-poly.cc
crawl-ref/source/mon-project.cc
crawl-ref/source/mon-speak.cc
crawl-ref/source/mon-spell.h
crawl-ref/source/mon-tentacle.cc
crawl-ref/source/mon-transit.cc
crawl-ref/source/mon-util.cc
crawl-ref/source/mon-util.h
crawl-ref/source/monster.cc
crawl-ref/source/monster.h
crawl-ref/source/mutation.cc
crawl-ref/source/mutation.h
crawl-ref/source/newgame.cc
crawl-ref/source/ng-init.cc
crawl-ref/source/options.h
crawl-ref/source/orb.h
crawl-ref/source/ouch.cc
crawl-ref/source/ouch.h
crawl-ref/source/output.cc
crawl-ref/source/pattern.cc
crawl-ref/source/pattern.h
crawl-ref/source/player-act.cc
crawl-ref/source/player-equip.cc
crawl-ref/source/player-reacts.cc
crawl-ref/source/player-stats.cc
crawl-ref/source/player-stats.h
crawl-ref/source/player.cc
crawl-ref/source/player.h
crawl-ref/source/potion.cc
crawl-ref/source/prompt.h
crawl-ref/source/quiver.cc
crawl-ref/source/random-var.h
crawl-ref/source/random-weight.h
crawl-ref/source/random.cc
crawl-ref/source/ranged_attack.h
crawl-ref/source/religion.cc
crawl-ref/source/religion.h
crawl-ref/source/rltiles/dc-mon.txt
crawl-ref/source/rltiles/dc-spells.txt
crawl-ref/source/rltiles/dc-wall.txt
crawl-ref/source/rltiles/dngn/wall/vault_stone00.png
crawl-ref/source/rltiles/dngn/wall/vault_stone01.png
crawl-ref/source/rltiles/dngn/wall/vault_stone02.png
crawl-ref/source/rltiles/dngn/wall/vault_stone03.png
crawl-ref/source/rltiles/dngn/wall/vault_stone04.png
crawl-ref/source/rltiles/dngn/wall/vault_stone05.png
crawl-ref/source/rltiles/dngn/wall/vault_stone06.png
crawl-ref/source/rltiles/dngn/wall/vault_stone07.png
crawl-ref/source/rltiles/dngn/wall/vault_stone08.png
crawl-ref/source/rltiles/dngn/wall/vault_stone09.png
crawl-ref/source/rltiles/dngn/wall/vault_stone10.png
crawl-ref/source/rltiles/dngn/wall/vault_stone11.png
crawl-ref/source/rltiles/dngn/wall/vault_stone12.png [new file with mode: 0644]
crawl-ref/source/rltiles/dngn/wall/vault_stone13.png [new file with mode: 0644]
crawl-ref/source/rltiles/dngn/wall/vault_stone14.png [new file with mode: 0644]
crawl-ref/source/rltiles/dngn/wall/vault_stone15.png [new file with mode: 0644]
crawl-ref/source/rltiles/gui/spells/necromancy/cigotuvis_embrace.png [new file with mode: 0644]
crawl-ref/source/rltiles/gui/spells/translocation/singularity.png [new file with mode: 0644]
crawl-ref/source/rltiles/misc/mdam_almost_dead.png
crawl-ref/source/rltiles/misc/mdam_heavily_damaged.png
crawl-ref/source/rltiles/misc/mdam_lightly_damaged.png
crawl-ref/source/rltiles/misc/mdam_moderately_damaged.png
crawl-ref/source/rltiles/misc/mdam_severely_damaged.png
crawl-ref/source/rltiles/mon/abyss/wretched_star.png
crawl-ref/source/rltiles/mon/big_kobold.png
crawl-ref/source/rltiles/mon/goblin.png
crawl-ref/source/rltiles/mon/hobgoblin.png
crawl-ref/source/rltiles/mon/holy/daeva.png
crawl-ref/source/rltiles/mon/holy/seraph.png
crawl-ref/source/rltiles/mon/nonliving/singularity1.png [new file with mode: 0644]
crawl-ref/source/rltiles/mon/nonliving/singularity2.png [new file with mode: 0644]
crawl-ref/source/rltiles/mon/nonliving/singularity3.png [new file with mode: 0644]
crawl-ref/source/rltiles/mon/nonliving/singularity4.png [new file with mode: 0644]
crawl-ref/source/sacrifice-data.h
crawl-ref/source/shopping.cc
crawl-ref/source/shopping.h
crawl-ref/source/shout.cc
crawl-ref/source/shout.h
crawl-ref/source/skill_menu.cc
crawl-ref/source/spl-book.cc
crawl-ref/source/spl-book.h
crawl-ref/source/spl-cast.cc
crawl-ref/source/spl-cast.h
crawl-ref/source/spl-clouds.cc
crawl-ref/source/spl-damage.cc
crawl-ref/source/spl-data.h
crawl-ref/source/spl-goditem.cc
crawl-ref/source/spl-miscast.cc
crawl-ref/source/spl-miscast.h
crawl-ref/source/spl-other.cc
crawl-ref/source/spl-selfench.cc
crawl-ref/source/spl-selfench.h
crawl-ref/source/spl-summoning.cc
crawl-ref/source/spl-summoning.h
crawl-ref/source/spl-tornado.cc
crawl-ref/source/spl-transloc.cc
crawl-ref/source/spl-transloc.h
crawl-ref/source/spl-util.cc
crawl-ref/source/spl-util.h
crawl-ref/source/spl-wpnench.cc
crawl-ref/source/sqldbm.cc
crawl-ref/source/startup.cc
crawl-ref/source/stash.cc
crawl-ref/source/stash.h
crawl-ref/source/state.cc
crawl-ref/source/status.cc
crawl-ref/source/status.h
crawl-ref/source/store.cc
crawl-ref/source/syscalls.cc
crawl-ref/source/tag-version.h
crawl-ref/source/tags.cc
crawl-ref/source/target.cc
crawl-ref/source/terrain.cc
crawl-ref/source/terrain.h
crawl-ref/source/throw.cc
crawl-ref/source/throw.h
crawl-ref/source/tilebuf.cc
crawl-ref/source/tilebuf.h
crawl-ref/source/tiledoll.cc
crawl-ref/source/tilemcache.cc
crawl-ref/source/tilemcache.h
crawl-ref/source/tilepick.cc
crawl-ref/source/tilereg-crt.cc
crawl-ref/source/tilereg-dgn.cc
crawl-ref/source/tilereg-grid.cc
crawl-ref/source/tilereg-map.cc
crawl-ref/source/tilereg-mon.cc
crawl-ref/source/tilereg-tab.cc
crawl-ref/source/tilereg-text.cc
crawl-ref/source/tilesdl.cc
crawl-ref/source/tilesdl.h
crawl-ref/source/tiletex.cc
crawl-ref/source/tiletex.h
crawl-ref/source/tileweb-text.cc
crawl-ref/source/tileweb.cc
crawl-ref/source/transform.cc
crawl-ref/source/traps.cc
crawl-ref/source/traps.h
crawl-ref/source/travel.cc
crawl-ref/source/travel.h
crawl-ref/source/view.cc
crawl-ref/source/view.h
crawl-ref/source/viewgeom.cc
crawl-ref/source/viewmap.cc
crawl-ref/source/webserver/game_data/static/dungeon_renderer.js
crawl-ref/source/webserver/game_data/static/util.js
crawl-ref/source/windowmanager-sdl.cc
crawl-ref/source/windowmanager-sdl.h
crawl-ref/source/windowmanager.h
crawl-ref/source/wiz-dump.cc
crawl-ref/source/wiz-dump.h
crawl-ref/source/wiz-fsim.cc
crawl-ref/source/wiz-item.cc
crawl-ref/source/wiz-mon.cc
crawl-ref/source/wiz-you.cc
crawl-ref/source/xom.cc
crawl-ref/source/xom.h
crawl-ref/source/zap-data.h
crawl-ref/source/zotdef.cc

index 03efb7f..dc3f678 100644 (file)
@@ -42,12 +42,12 @@ sub try {
             error(1);
         }
         elsif ($? & 127) {
-            printf "'$cmd' died with signal %d", ($? & 127);
+            printf "'$cmd' died with signal %d\n", ($? & 127);
             error(1);
         }
         elsif ($?) {
             my $exit = $? >> 8;
-            printf "'$cmd' returned exit value %d", $exit;
+            printf "'$cmd' returned exit value %d\n", $exit;
             error($exit);
         }
     }
index 8e13a2a..9295a5f 100644 (file)
@@ -83,7 +83,7 @@
     Max Bane
     Bill Beher
     Alexander Beisig
-    Edgar Bering
+    Edgar A. Bering IV
     Stu Black
     blackcustard
     Peter 'xFleury' Blain
index f95bbd7..c81bd6d 100644 (file)
@@ -1,4 +1,4 @@
-Stone Soup 0.16 (up to 0.16-a0-2405-g490e8e2)
+Stone Soup 0.16 (up to 0.16-a0-2728-gd75f5fb)
 --------------------------------------------
 * New gods: Gozag Ym Sagoz the Greedy and Ru the Awakened.
 
@@ -11,6 +11,8 @@ Branches, environment
 * Clouds of draining now require rN+++ for immunity, up from rN+.
 * Clouds of miasma now apply the 'Rot' status, rather than causing rotting
   directly.
+* Traps no longer care about whether the player is flying.
+* The 'disarm trap' command has been removed.
 
 Character
 ---------
@@ -22,6 +24,7 @@ Character
 * Ghouls now gain as much healing from normal chunks as they previously got
   from rotting flesh, and always cure a point of rot when eating a chunk.
 * Vampires now bottle blood with 'c', which they can now do from first level.
+* Butchering/bottling now only takes a single turn.
 * Wanderers now receive a randart spellbook instead of starting with a Level 1
   spell memorized.
 * Wanderers can start with one elemental evoker or a box of beasts instead of
@@ -44,9 +47,18 @@ Monsters
 * Curse skulls now move - fast!
 * Iron devils have been upgraded to rust devils, which do less damage but
   corrode with their touch.
+* Insubstantial wisps are now more fragile & less numerous, but have Static
+  Discharge.
+* Seraphim are now considerably stronger, and have a new spellset. They can
+  now (very occasionally) appear on the orb run.
 * New monsters:
+  - Robin of the Strong Arm, appearing in the early Dungeon. An unusually
+    strong hobgoblin, with a personal army of hobgoblins and goblins. She
+    happily hurls the latter at her enemies.
   - Caustic shrikes, appearing in Depths. Metallic birds that travel in flocks,
     attacking with acid-dripping claws that sear through armour & flesh alike.
+  - Shard shrikes, appearing in Cocytus. Batty monsters that come in flocks and
+    spit deadly ice shards at their foes.
   - Elemental wellsprings, appearing in Cocytus. They fire powerful waves of
     water which produce hostile water elementals - but deplete their own
     'health' in the process.
@@ -65,10 +77,12 @@ Monsters
   - Green rats -> river rats.
   - Mermaids -> sirens.
   - Sirens -> merfolk avatars.
+* Ghost crabs have been moved from Crypt to Swamp.
 * The "misshapen and mutated" status (from Malmutate and the new Irradiate
   spell) is now temporary, but more significant.
 * Player ghosts & spellforged servitors can now have more than six spells.
 * Player ghosts now have their weapon brands displayed.
+* Hellwings can now cast Cigotuvi's Embrace, and start with it active.
 * Xtahua's breath now creates a cloud of flames, like red draconians'.
 * Tiamat can now be any colour of draconian, except grey.
 * Fannar can now cast Condensation Shield.
@@ -77,18 +91,28 @@ Monsters
   invisible + blindness immunity instead.
 * Magic immunity no longer confers confusion immunity to monsters.
 * Polymorphed monsters now return to their original shapes in death.
+* Liches (and ancient liches) now choose their spells randomly from a large
+  set of possible spells, instead of having fixed spellbooks.
 
 Spells
 ------
 * New spells:
+  - Singularity (L9 Translocation), which summons a singularity & sucks in all
+    nearby enemies, severely damaging them in the process.
   - Hydra Form (L6 Transmutation), a short-duration form that attacks all
     adjacent foes & devours slain foes for nutrition and healing.
   - Irradiate (L5 Transmutation/Conjuration), which deals heavy damage &
     malmutates nearby enemies, but also contaminates the caster.
+  - Cigotuvi's Embrace (L5 Necromancy), which turns all corpses in LOS into
+    armour for the player.
+* Dispersal now affects a wider radius & has a chance of confusing its victims.
+* Force Lance is now L4 Translocation/Conjuration, and is more accurate and
+  better at knocking enemies back.
 * Ice, Dragon, Tree and Fungus forms no longer meld octopode rings.
 * Dragon Form now provides a spell power & success chance enhancer for Dragon's
   Call.
 * Spell miscast effects have been hugely revamped.
+* Twisted Resurrection has been removed.
 
 Items
 -----
@@ -113,6 +137,7 @@ Items
   - The Iron Rod, which fires a spray of shrapnel in a wide arc. Extremely
     effective against agile, low-AC enemies, but ineffective against armoured
     enemies or at a distance.
+  - The Plane Papyrus, a new high-level Translocations book.
 * Regeneration has been moved from rings to amulets.
 * Corrosion resistance can now occur on artefact armour.
 * Scale mail now only has 10 ER, down from 11.
@@ -186,6 +211,9 @@ Cards
   - Velocity will always apply a overall-positive effect (with allies present).
   - Banshee now drains victims, in addition to causing fear.
   - Solitude can now cause Disjunction at high power.
+* Wild Magic now affects enemies instead of the player, and restores magic for
+  each enemy so affected. It has moved from the Deck of Punishment to the Deck
+  of Wonders.
 * Dowsing is no longer in the deck of battle.
 * Decks of war have lost their set of destruction cards.
 * The Cloud card has moved from decks of destruction to decks of emergency.
@@ -250,6 +278,7 @@ Interface
   are still visible with @.)
 * A third page has been added to the god description (^) screen, listing causes
   and effects of divine wrath.
+* Control-direction no longer tries to open or close doors.
 * Derived undead (zombies, skeletons, &c) now have their full names displayed.
 * Corrosion now visibly (temporarily) reduces weapons' enhancement bonuses.
 * Monsters no longer appear on the minimap.
@@ -257,6 +286,11 @@ Interface
   specified in the init file, causing one to be randomly selected; a new combo
   option allows entire sets of the above to be specified, again with one being
   randomly selected.
+* Added explore mode, which is a limited version of wizard mode, where the
+  only benefit gained is the inability to die. No extra information is
+  displayed (like in wizard mode), and no extra commands are available. As
+  with wizard mode, explore mode games are not scored. Enter explore mode
+  using '+'.
 
 Stone Soup 0.15.2 (20141018)
 ----------------------------
index 15cbb54..601dff4 100644 (file)
@@ -525,7 +525,8 @@ ITEM:     (list of items, separated by comma)
           * "damaged" sets the item plusses to -1..-4. This modifier (and
             the corresponding effect of the "cursed" modifier) does not
             affect jewellery.
-          * "cursed" gets a curse plus plusses as in "damaged".
+          * "cursed" gets a curse plus plusses as in "damaged"; but the latter
+            may be overridden with "mundane", "good_item", "level:N", etc.
           * "not_cursed" forces an item to have any curse placed on it to be
             removed.
           * "any" by itself gives a random choice; you can combine "any" with
index 2a3925f..e8de6ab 100644 (file)
@@ -121,9 +121,9 @@ The contents of this text are:
                 note_chat_messages, note_dgl_messages
 5-  Miscellaneous.
 5-a     All OS.
-                mouse_input, wiz_mode, char_set, colour, display_char, feature,
-                mon_glyph, item_glyph, use_fake_player_cursor,
-                show_player_species
+                mouse_input, wiz_mode, explore_mode, char_set, colour,
+                display_char, feature, mon_glyph, item_glyph,
+                use_fake_player_cursor, show_player_species
 
 5-b     DOS and Windows.
                 dos_use_background_intensity
@@ -360,7 +360,7 @@ restart_after_game = true/false
 
 restart_after_save = false
         When the game is saved, return to the main menu. This option
-        is only effective if restart_after_game is also enabled.
+        has no effect unless restart_after_game is also enabled.
 
 default_manual_training = false
         When set to true, new characters will start with skill training
@@ -2113,6 +2113,13 @@ wiz_mode = (no | never | yes)
                    of game
           never -- never allow a wizard command to be used
 
+explore_mode = (no | never | yes)
+        Explore mode options (available only in WIZARD compiles):
+          yes   -- start games in explore mode (game will not be scored)
+          no    -- still allows player to enter explore mode after start
+                   of game
+          never -- never allow explore mode to be entered
+
 char_set = (default | ascii)
         Chooses different pre-set character sets for the game play screen.
         Unlike previous versions of Crawl, this does not select the I/O
index 13f0858..bc0c147 100644 (file)
@@ -63,6 +63,9 @@
 # Override the vi movement keys with a non-command.
 # include = no_vi_command_keys.txt
 
+# Turn the shift-vi keys into safe move, instead of run.
+# include = safe_move_shift.txt
+
 ##### Ancient versions ##############################################
 # If you're used to the interface of ancient versions of Crawl, you may
 # get back parts of it by uncommenting the following options:
diff --git a/crawl-ref/settings/safe_move_shift.txt b/crawl-ref/settings/safe_move_shift.txt
new file mode 100644 (file)
index 0000000..e7a4ad5
--- /dev/null
@@ -0,0 +1,9 @@
+# Rebind shift-move to safe moving.
+bindkey = [H] CMD_SAFE_MOVE_LEFT
+bindkey = [J] CMD_SAFE_MOVE_DOWN
+bindkey = [K] CMD_SAFE_MOVE_UP
+bindkey = [L] CMD_SAFE_MOVE_RIGHT
+bindkey = [Y] CMD_SAFE_MOVE_UP_LEFT
+bindkey = [U] CMD_SAFE_MOVE_UP_RIGHT
+bindkey = [B] CMD_SAFE_MOVE_DOWN_LEFT
+bindkey = [N] CMD_SAFE_MOVE_DOWN_RIGHT
index 76c83ae..435422d 100644 (file)
@@ -270,6 +270,7 @@ perl.exe "util/gen-cflg.pl" compflag.h "&lt;UNKNOWN&gt;" "&lt;UNKNOWN&gt;"
     <ClCompile Include="..\delay.cc" />\r
     <ClCompile Include="..\describe.cc" />\r
     <ClCompile Include="..\describe-god.cc" />\r
+    <ClCompile Include="..\describe-spells.cc" />
     <ClCompile Include="..\dgl-message.cc" />\r
     <ClCompile Include="..\dgn-delve.cc" />\r
     <ClCompile Include="..\dgn-height.cc" />\r
@@ -658,6 +659,7 @@ perl.exe "util/gen-cflg.pl" compflag.h "&lt;UNKNOWN&gt;" "&lt;UNKNOWN&gt;"
     <ClInclude Include="..\delay.h" />\r
     <ClInclude Include="..\describe.h" />\r
     <ClInclude Include="..\describe-god.h" />\r
+    <ClInclude Include="..\describe-spells.h" />
     <ClInclude Include="..\dgl-message.h" />\r
     <ClInclude Include="..\dgn-delve.h" />\r
     <ClInclude Include="..\dgn-height.h" />\r
index 610500f..1c29d83 100644 (file)
@@ -40,6 +40,7 @@
     <ClCompile Include="..\delay.cc" />\r
     <ClCompile Include="..\describe.cc" />\r
     <ClCompile Include="..\describe-god.cc" />\r
+    <ClCompile Include="..\describe-spells.cc" />
     <ClCompile Include="..\dgl-message.cc" />\r
     <ClCompile Include="..\dgn-delve.cc" />\r
     <ClCompile Include="..\dgn-height.cc" />\r
     <ClInclude Include="..\delay.h" />\r
     <ClInclude Include="..\describe.h" />\r
     <ClInclude Include="..\describe-god.h" />\r
+    <ClInclude Include="..\describe-spells.h" />
     <ClInclude Include="..\dgl-message.h" />\r
     <ClInclude Include="..\dgn-delve.h" />\r
     <ClInclude Include="..\dgn-height.h" />\r
index 788d02d..9b3de3f 100644 (file)
@@ -757,7 +757,7 @@ CFWARN_L += $(shell w=-Wno-format-zero-length;echo|$(GXX) -E -x c++ - -Werror $$
 CFWARN_L += $(shell w=-Wmissing-declarations;echo|$(GXX) -E -x c++ - -Werror $$w >/dev/null 2>&1 && echo $$w)
 CFWARN_L  += $(shell w=-Wredundant-decls;echo|$(GXX) -E -x c++ - -Werror $$w >/dev/null 2>&1 && echo $$w)
 
-CFWARN_L += -Wno-parentheses -Wno-unused-parameter -Wwrite-strings -Wshadow
+CFWARN_L += -Wno-parentheses -Wwrite-strings -Wshadow
 CFOTHERS_L = $(EXTERNAL_FLAGS_L) $(EXTRA_FLAGS) $(DEFINES) $(SDL2_CFLAGS)
 
 ifndef NO_LUA_BINDINGS
index 1cd2c53..30441dd 100644 (file)
@@ -39,6 +39,7 @@ decks.o \
 delay.o \
 describe.o \
 describe-god.o \
+describe-spells.o \
 dgl-message.o \
 dgn-delve.o \
 dgn-height.o \
index e89f974..38b7cb4 100644 (file)
@@ -1617,7 +1617,8 @@ static bool _check_ability_possible(const ability_def& abil,
 
     case ABIL_OKAWARU_FINESSE:
         if (stasis_blocks_effect(false,
-                                 quiet ? NULL : "%s makes your neck tingle."))
+                                 quiet ? nullptr
+                                       : "%s makes your neck tingle."))
         {
             return false;
         }
@@ -2131,14 +2132,14 @@ static spret_type _do_ability(const ability_def& abil, bool fail)
         targetter_beam tgt(&you, beam.range, ZAP_FIREBALL, power, 1, 1);
 
         if (!spell_direction(spd, beam, DIR_NONE, TARG_HOSTILE, beam.range,
-                             true, true, false, NULL,
+                             true, true, false, nullptr,
                              "Aiming: <white>Delayed Fireball</white>",
                              false, &tgt))
         {
             return SPRET_ABORT;
         }
 
-        if (!zapping(ZAP_FIREBALL, power, beam, true, NULL, false))
+        if (!zapping(ZAP_FIREBALL, power, beam, true, nullptr, false))
             return SPRET_ABORT;
 
         // Only one allowed, since this is instantaneous. - bwr
@@ -2177,7 +2178,7 @@ static spret_type _do_ability(const ability_def& abil, bool fail)
         beam.range = 1;
         if (!spell_direction(abild, beam,
                              DIR_NONE, TARG_HOSTILE, 0, true, true, false,
-                             NULL, NULL, false,
+                             nullptr, nullptr, false,
                              &hitfunc))
         {
             return SPRET_ABORT;
@@ -2458,7 +2459,7 @@ static spret_type _do_ability(const ability_def& abil, bool fail)
 
         monster* mons = monster_at(beam.target);
 
-        if (mons == NULL || !you.can_see(mons))
+        if (mons == nullptr || !you.can_see(mons))
         {
             mpr("There is no monster there to imprison!");
             return SPRET_ABORT;
@@ -2578,7 +2579,7 @@ static spret_type _do_ability(const ability_def& abil, bool fail)
         }
 
         monster* mons = monster_at(beam.target);
-        if (mons == NULL || !you.can_see(mons)
+        if (mons == nullptr || !you.can_see(mons)
             || !ench_flavour_affects_monster(BEAM_ENSLAVE_SOUL, mons))
         {
             mpr("You see nothing there to enslave the soul of!");
@@ -2593,7 +2594,7 @@ static spret_type _do_ability(const ability_def& abil, bool fail)
         }
         fail_check();
 
-        return zapping(ZAP_ENSLAVE_SOUL, power, beam, false, NULL, fail);
+        return zapping(ZAP_ENSLAVE_SOUL, power, beam, false, nullptr, fail);
     }
 
     case ABIL_SIF_MUNA_CHANNEL_ENERGY:
@@ -2834,7 +2835,7 @@ static spret_type _do_ability(const ability_def& abil, bool fail)
         }
 
         return zapping(ZAP_BANISHMENT, 16 + you.skill(SK_INVOCATIONS, 8), beam,
-                       true, NULL, fail);
+                       true, nullptr, fail);
 
     case ABIL_LUGONU_CORRUPT:
         fail_check();
@@ -3236,7 +3237,7 @@ static void _pay_ability_costs(const ability_def& abil, int zpcost)
 
     if (abil.flags & ABFLAG_NECRO_MISCAST_MINOR)
     {
-        MiscastEffect(&you, NULL, ABIL_MISCAST, SPTYP_NECROMANCY, 5, 90,
+        MiscastEffect(&you, nullptr, ABIL_MISCAST, SPTYP_NECROMANCY, 5, 90,
                       "power out of control");
     }
     if (abil.flags & ABFLAG_LEVEL_DRAIN)
@@ -3261,7 +3262,7 @@ int choose_ability_menu(const vector<talent>& talents)
                              | MF_TOGGLE_ACTION | MF_ALWAYS_SHOW_MORE,
                              text_only);
 
-    abil_menu.set_highlighter(NULL);
+    abil_menu.set_highlighter(nullptr);
 #ifdef USE_TILE_LOCAL
     {
         // Hack like the one in spl-cast.cc:list_spells() to align the title.
@@ -3916,9 +3917,9 @@ vector<ability_type> get_god_abilities(bool include_unusable, bool ignore_piety)
     else if (you_worship(GOD_RU))
     {
 
-        ASSERT(you.props.exists("available_sacrifices"));
+        ASSERT(you.props.exists(AVAILABLE_SAC_KEY));
         CrawlVector &available_sacrifices
-            = you.props["available_sacrifices"].get_vector();
+            = you.props[AVAILABLE_SAC_KEY].get_vector();
 
         int num_sacrifices = available_sacrifices.size();
         for (int i = 0; i < num_sacrifices; ++i)
index 56dd2bd..bd67abd 100644 (file)
@@ -391,7 +391,7 @@ void push_features_to_abyss()
 
             dungeon_feature_type feature = map_bounds(p) ? grd(p) : DNGN_UNSEEN;
             feature = sanitize_feature(feature);
-                 abyssal_features.push_back(feature);
+            abyssal_features.push_back(feature);
         }
     }
 }
@@ -1021,7 +1021,7 @@ static ProceduralSample _abyss_grid(const coord_def &p)
         return sample;
     }
 
-    if (abyssLayout == NULL)
+    if (abyssLayout == nullptr)
     {
         const level_id lid = _get_random_level();
         levelLayout = new LevelLayout(lid, 5, rivers);
@@ -1244,13 +1244,13 @@ static void _abyss_apply_terrain(const map_bitmask &abyss_genlevel_mask,
         you.char_direction != GDT_GAME_START
         && _abyss_check_place_feat(p, 10000,
                                    &altars_wanted,
-                                   NULL,
+                                   nullptr,
                                    _abyss_pick_altar(),
                                    abyss_genlevel_mask)
         ||
         you.char_direction != GDT_GAME_START
         && level_id::current().depth < brdepth[BRANCH_ABYSS]
-        && _abyss_check_place_feat(p, 1900, NULL, NULL,
+        && _abyss_check_place_feat(p, 1900, nullptr, nullptr,
                                    DNGN_ABYSSAL_STAIR,
                                    abyss_genlevel_mask);
     }
index 47788b4..ea25602 100644 (file)
@@ -994,13 +994,6 @@ static bool _do_book_acquirement(item_def &book, int agent)
                 continue;
             }
 
-#if TAG_MAJOR_VERSION == 34
-            if (bk == BOOK_STALKING)
-            {
-                weights[bk] = 0;
-                continue;
-            }
-#endif
             weights[bk]    = _book_weight(static_cast<book_type>(bk));
             total_weights += weights[bk];
         }
@@ -1525,7 +1518,7 @@ bool acquirement(object_class_type class_wanted, int agent,
 
     int thing_created = NON_ITEM;
 
-    if (item_index == NULL)
+    if (item_index == nullptr)
         item_index = &thing_created;
 
     *item_index = NON_ITEM;
index 97015d2..39ff774 100644 (file)
@@ -7,7 +7,7 @@
 #define ACQUIRE_H
 
 bool acquirement(object_class_type force_class, int agent,
-                 bool quiet = false, int *item_index = NULL,
+                 bool quiet = false, int *item_index = nullptr,
                  bool debug = false);
 
 int acquirement_create_item(object_class_type class_wanted,
index 6ff360d..2e6e63c 100644 (file)
@@ -7,11 +7,13 @@
 #include "act-iter.h"
 #include "areas.h"
 #include "attack.h"
+#include "directn.h"
 #include "env.h"
 #include "fprop.h"
 #include "itemprop.h"
 #include "los.h"
 #include "misc.h"
+#include "mon-behv.h"
 #include "mon-death.h"
 #include "religion.h"
 #include "stepdown.h"
@@ -71,13 +73,13 @@ hands_reqd_type actor::hands_reqd(const item_def &item) const
 
 /**
  * Wrapper around the virtual actor::can_wield(const item_def&,bool,bool,bool,bool) const overload.
- * @param item May be NULL, in which case a dummy item will be passed in.
+ * @param item May be nullptr, in which case a dummy item will be passed in.
  */
 bool actor::can_wield(const item_def* item, bool ignore_curse,
                       bool ignore_brand, bool ignore_shield,
                       bool ignore_transform) const
 {
-    if (item == NULL)
+    if (item == nullptr)
     {
         // Unarmed combat.
         item_def fake;
@@ -111,7 +113,7 @@ bool actor::handle_trap()
     trap_def* trap = find_trap(pos());
     if (trap)
         trap->trigger(*this);
-    return trap != NULL;
+    return trap != nullptr;
 }
 
 int actor::skill_rdiv(skill_type sk, int mult, int div) const
@@ -663,7 +665,8 @@ void actor::handle_constriction()
         damage = timescale_damage(this, damage);
         DIAG_ONLY(const int timescale_dam = damage);
 
-        damage = defender->hurt(this, damage, BEAM_MISSILE, false);
+        damage = defender->hurt(this, damage, BEAM_MISSILE,
+                                KILLED_BY_MONSTER, "", "", false);
         DIAG_ONLY(const int infdam = damage);
 
         string exclamations;
@@ -838,3 +841,59 @@ string actor::resist_margin_phrase(int margin) const
     return make_stringf(resist_messages[index][0].c_str(),
                         conj_verb(resist_messages[index][1]).c_str());
 }
+
+void actor::collide(coord_def newpos, const actor *agent, int pow)
+{
+    actor *other = actor_at(newpos);
+    ASSERT(this != other);
+
+    if (is_monster())
+        behaviour_event(as_monster(), ME_WHACK, agent);
+
+    dice_def damage(2, 1 + pow / 10);
+
+    if (other)
+    {
+        if (other->is_monster())
+            behaviour_event(other->as_monster(), ME_WHACK, agent);
+        if (you.can_see(this) || you.can_see(other))
+        {
+            mprf("%s %s with %s!",
+                 name(DESC_THE).c_str(),
+                 conj_verb("collide").c_str(),
+                 other->name(DESC_THE).c_str());
+        }
+        const string thisname = name(DESC_A, true);
+        const string othername = other->name(DESC_A, true);
+        other->hurt(agent, other->apply_ac(damage.roll()),
+                    BEAM_MISSILE, KILLED_BY_COLLISION,
+                    "", thisname);
+        if (alive())
+        {
+            hurt(agent, apply_ac(damage.roll()), BEAM_MISSILE,
+                 KILLED_BY_COLLISION, "", othername);
+        }
+        return;
+    }
+
+    if (you.can_see(this))
+    {
+        if (!can_pass_through_feat(grd(newpos)))
+        {
+            mprf("%s %s into %s!",
+                 name(DESC_THE).c_str(), conj_verb("slam").c_str(),
+                 env.map_knowledge(newpos).known()
+                 ? feature_description_at(newpos, false, DESC_THE, false)
+                       .c_str()
+                 : "something");
+        }
+        else
+        {
+            mprf("%s violently %s moving!",
+                 name(DESC_THE).c_str(), conj_verb("stop").c_str());
+        }
+    }
+    hurt(agent, apply_ac(damage.roll()), BEAM_MISSILE,
+         KILLED_BY_COLLISION, "",
+         feature_description_at(newpos, false, DESC_A, false));
+}
index df9eaa4..152266e 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "itemprop-enum.h"
 #include "random-var.h"
+#include "ouch.h"
 
 enum ev_ignore_type
 {
@@ -42,8 +43,8 @@ public:
     virtual bool      alive() const = 0;
 
     // Should return false for perma-summoned things.
-    virtual bool is_summoned(int* duration = NULL,
-                             int* summon_type = NULL) const = 0;
+    virtual bool is_summoned(int* duration = nullptr,
+                             int* summon_type = nullptr) const = 0;
 
     virtual bool is_perm_summoned() const = 0;
 
@@ -101,7 +102,7 @@ public:
         return weapon(0);
     }
     virtual random_var attack_delay(const item_def *weapon,
-                                    const item_def *projectile = NULL,
+                                    const item_def *projectile = nullptr,
                                     bool random = true, bool scaled = true,
                                     bool shield = true)
                                    const = 0;
@@ -115,7 +116,7 @@ public:
                             bool calc_unid = true) const = 0;
     virtual int scan_artefacts(artefact_prop_type which_property,
                                bool calc_unid = true,
-                               vector<item_def> *matches = NULL) const = 0;
+                               vector<item_def> *matches = nullptr) const = 0;
 
     virtual hands_reqd_type hands_reqd(const item_def &item) const;
 
@@ -151,9 +152,9 @@ public:
     virtual string pronoun(pronoun_type which_pronoun,
                            bool force_visible = false) const = 0;
     virtual string conj_verb(const string &verb) const = 0;
-    virtual string hand_name(bool plural, bool *can_plural = NULL) const = 0;
-    virtual string foot_name(bool plural, bool *can_plural = NULL) const = 0;
-    virtual string arm_name(bool plural, bool *can_plural = NULL) const = 0;
+    virtual string hand_name(bool plural, bool *can_plural = nullptr) const = 0;
+    virtual string foot_name(bool plural, bool *can_plural = nullptr) const = 0;
+    virtual string arm_name(bool plural, bool *can_plural = nullptr) const = 0;
 
     virtual bool fumbles_attack() = 0;
 
@@ -203,6 +204,9 @@ public:
                      bool quiet = false) = 0;
     virtual int  hurt(const actor *attacker, int amount,
                       beam_type flavour = BEAM_MISSILE,
+                      kill_method_type kill_type = KILLED_BY_MONSTER,
+                      string source = "",
+                      string aux = "",
                       bool cleanup_dead = true,
                       bool attacker_effects = true) = 0;
     virtual bool heal(int amount, bool max_too = false) = 0;
@@ -225,7 +229,7 @@ public:
     virtual void drain_stat(stat_type stat, int amount, actor* attacker) { }
     virtual void splash_with_acid(const actor* evildoer, int acid_strength = -1,
                                   bool allow_corrosion = true,
-                                  const char* hurt_msg = NULL) = 0;
+                                  const char* hurt_msg = nullptr) = 0;
 
     virtual bool can_hibernate(bool holi_only = false,
                                bool intrinsic_only = false) const;
@@ -310,7 +314,8 @@ public:
     virtual bool gourmand(bool calc_unid = true, bool items = true) const;
 
     virtual bool res_corr(bool calc_unid = true, bool items = true) const;
-    bool has_notele_item(bool calc_unid = true, vector<item_def> *matches = NULL) const;
+    bool has_notele_item(bool calc_unid = true,
+                         vector<item_def> *matches = nullptr) const;
     virtual bool stasis(bool calc_unid = true, bool items = true) const;
     virtual bool run(bool calc_unid = true, bool items = true) const;
     virtual bool angry(bool calc_unid = true, bool items = true) const;
@@ -416,7 +421,7 @@ public:
 
     // Map from mid to duration.
     typedef map<mid_t, int> constricting_t;
-    // Freed and set to NULL when empty.
+    // Freed and set to nullptr when empty.
     constricting_t *constricting;
 
     void start_constricting(actor &whom, int duration = 0);
@@ -444,6 +449,8 @@ public:
 
     string resist_margin_phrase(int margin) const;
 
+    void collide(coord_def newpos, const actor *agent, int pow);
+
 private:
     void end_constriction(mid_t whom, bool intentional, bool quiet);
 };
index 4cbedf4..a7c3650 100644 (file)
@@ -152,6 +152,18 @@ static void _actor_areas(actor *a)
         no_areas = false;
     }
 #endif
+
+    // XXX: make this a proper function
+    if (a->type == MONS_SINGULARITY)
+    {
+        r = a->get_experience_level();
+
+        _agrid_centres.push_back(area_centre(AREA_DISJUNCTION, a->pos(), r));
+
+        for (radius_iterator ri(a->pos(), r, C_CIRCLE, LOS_NO_TRANS); ri; ++ri)
+            _set_agrid_flag(*ri, APROP_DISJUNCTION);
+        no_areas = false;
+    }
 }
 
 /**
@@ -179,7 +191,6 @@ static void _update_agrid()
 
     if (you_worship(GOD_GOZAG))
     {
-        const int r = 2;
         for (rectangle_iterator ri(0); ri; ++ri)
         {
             // ASSUMPTION: gold will always be on the top of the pile.
@@ -187,12 +198,8 @@ static void _update_agrid()
                 && mitm[igrd(*ri)].special > 0)
             {
                 no_areas = false;
-                _agrid_centres.emplace_back(AREA_GOLD, *ri, r);
-                for (radius_iterator rdi(*ri, r, C_CIRCLE, LOS_NO_TRANS);
-                     rdi; ++rdi)
-                {
-                    _set_agrid_flag(*rdi, APROP_GOLD);
-                }
+                _agrid_centres.emplace_back(AREA_GOLD, *ri, 0);
+                _set_agrid_flag(*ri, APROP_GOLD);
             }
         }
     }
@@ -409,7 +416,7 @@ void create_sanctuary(const coord_def& center, int time)
     int       trap_count  = 0;
     int       scare_count = 0;
     int       cloud_count = 0;
-    monster* seen_mon    = NULL;
+    monster* seen_mon    = nullptr;
 
     // Since revealing mimics can move monsters, we do it first.
     for (radius_iterator ri(center, radius, C_POINTY); ri; ++ri)
@@ -513,7 +520,7 @@ void create_sanctuary(const coord_def& center, int time)
     if (blood_count > 0)
         mprf(MSGCH_GOD, "By Zin's power, all blood is cleared from the sanctuary.");
 
-    if (scare_count == 1 && seen_mon != NULL)
+    if (scare_count == 1 && seen_mon != nullptr)
         simple_monster_message(seen_mon, " turns to flee the light!");
     else if (scare_count > 0)
         mpr("The monsters scatter in all directions!");
@@ -609,7 +616,7 @@ int monster::halo_radius2() const
         return size;
     // The values here depend on 1. power, 2. sentience.  Thus, high-ranked
     // sentient celestials have really big haloes, while holy animals get
-    // small ones.
+    // little or none.
     switch (type)
     {
     case MONS_ANGEL:
@@ -618,9 +625,9 @@ int monster::halo_radius2() const
         return 29;
     case MONS_DAEVA:
         return 32;
-    case MONS_SERAPH:
-        return 50;
     case MONS_OPHAN:
+        return 50;
+    case MONS_SERAPH:
         return 65; // highest rank among sentient ones
     case MONS_HOLY_SWINE:
         return 2;  // only notionally holy
index 0f03f87..03d7fed 100644 (file)
@@ -128,7 +128,7 @@ namespace arena
     static bool cycle_random     = false;
     static uint32_t cycle_random_pos = 0;
 
-    static FILE *file = NULL;
+    static FILE *file = nullptr;
     static level_id place(BRANCH_DEPTHS, 1);
 
     static void adjust_spells(monster* mons, bool no_summons, bool no_animate)
@@ -152,12 +152,13 @@ namespace arena
 
     static void list_eq(const monster *mon)
     {
-        if (!Options.arena_list_eq || file == NULL)
+        if (!Options.arena_list_eq || file == nullptr)
             return;
 
         vector<int> items;
-        copy_if(begin(mon->inv), end(mon->inv), begin(items),
-                [] (short it) { return it != NON_ITEM; });
+        for (short it : mon->inv)
+            if (it != NON_ITEM)
+                items.push_back(it);
 
         if (items.empty())
             return;
@@ -574,7 +575,7 @@ namespace arena
 
     static void dump_messages()
     {
-        if (!Options.arena_dump_msgs || file == NULL)
+        if (!Options.arena_dump_msgs || file == nullptr)
             return;
 
         vector<string> messages;
@@ -691,7 +692,7 @@ namespace arena
             return;
         }
 
-        if (file != NULL)
+        if (file != nullptr)
             fflush(file);
 
         cursor_control coff(true);
@@ -915,11 +916,11 @@ namespace arena
             game_ended_with_error(error);
         }
 
-        if (file != NULL)
+        if (file != nullptr)
             end(0, false, "Results file already open");
         file = fopen("arena.result", "w");
 
-        if (file != NULL)
+        if (file != nullptr)
         {
             string spec = find_monster_spec();
             fprintf(file, "%s\n", spec.c_str());
@@ -942,15 +943,15 @@ namespace arena
 
     static void global_shutdown()
     {
-        if (file != NULL)
+        if (file != nullptr)
             fclose(file);
 
-        file = NULL;
+        file = nullptr;
     }
 
     static void write_results()
     {
-        if (file != NULL)
+        if (file != nullptr)
         {
             if (Options.arena_dump_msgs || Options.arena_list_eq)
                 fprintf(file, "========================================\n");
@@ -964,12 +965,12 @@ namespace arena
 
     static void write_error(const string &error)
     {
-        if (file != NULL)
+        if (file != nullptr)
         {
             fprintf(file, "err: %s\n", error.c_str());
             fclose(file);
         }
-        file = NULL;
+        file = nullptr;
     }
 
     static void simulate()
@@ -1225,7 +1226,7 @@ void arena_monster_died(monster* mons, killer_type killer,
              && killer == KILL_MISC
              && killer_index == NON_MONSTER))
     {
-        arena::faction *fac = NULL;
+        arena::faction *fac = nullptr;
         if (mons->attitude == ATT_FRIENDLY)
             fac = &arena::faction_a;
         else if (mons->attitude == ATT_HOSTILE)
index 444249e..8f14e9f 100644 (file)
@@ -46,7 +46,7 @@ static void _equip_mpr(bool* show_msgs, const char* msg,
                        msg_channel_type chan = MSGCH_PLAIN)
 {
     bool do_show = true;
-    if (show_msgs == NULL)
+    if (show_msgs == nullptr)
         show_msgs = &do_show;
 
     if (*show_msgs)
@@ -153,7 +153,7 @@ static void _CURSES_equip(item_def *item, bool *show_msgs, bool unmeld)
     _equip_mpr(show_msgs, "A shiver runs down your spine.");
     if (!unmeld)
     {
-        MiscastEffect(&you, NULL, WIELD_MISCAST, SPTYP_NECROMANCY, random2(9),
+        MiscastEffect(&you, nullptr, WIELD_MISCAST, SPTYP_NECROMANCY, random2(9),
                       random2(70), "the Scythe of Curses", NH_NEVER);
     }
 }
@@ -327,7 +327,7 @@ static void _SINGING_SWORD_equip(item_def *item, bool *show_msgs, bool unmeld)
 {
     bool def_show = true;
 
-    if (show_msgs == NULL)
+    if (show_msgs == nullptr)
         show_msgs = &def_show;
 
     if (!*show_msgs)
@@ -450,7 +450,7 @@ static void _TROG_unequip(item_def *item, bool *show_msgs)
 
 static void _wucad_miscast(actor* victim, int power,int fail)
 {
-    MiscastEffect(victim, NULL, WIELD_MISCAST, SPTYP_DIVINATION, power, fail,
+    MiscastEffect(victim, nullptr, WIELD_MISCAST, SPTYP_DIVINATION, power, fail,
                   "the Staff of Wucad Mu", NH_NEVER);
 }
 
@@ -628,7 +628,7 @@ static monster* _find_nearest_possible_beholder()
         }
     }
 
-    return NULL;
+    return nullptr;
 }
 
 static void _DEMON_AXE_world_reacts(item_def *item)
@@ -982,7 +982,7 @@ static void _ELEMENTAL_STAFF_melee_effects(item_def*, actor* attacker,
     if (mondied || !(x_chance_in_y(evoc, 27*27) || x_chance_in_y(evoc, 27*27)))
         return;
 
-    const char *verb = NULL;
+    const char *verb = nullptr;
     beam_type flavour = BEAM_NONE;
 
     switch (random2(4))
index 495c442..2db78b4 100644 (file)
@@ -50,14 +50,14 @@ attack::attack(actor *attk, actor *defn, actor *blame)
       damage_done(0), special_damage(0), aux_damage(0), min_delay(0),
       final_attack_delay(0), special_damage_flavour(BEAM_NONE),
       stab_attempt(false), stab_bonus(0), apply_bleeding(false),
-      ev_margin(0), weapon(NULL),
+      ev_margin(0), weapon(nullptr),
       damage_brand(SPWPN_NORMAL), wpn_skill(SK_UNARMED_COMBAT),
-      shield(NULL), art_props(0), unrand_entry(NULL),
+      shield(nullptr), art_props(0), unrand_entry(nullptr),
       attacker_to_hit_penalty(0), attack_verb("bug"), verb_degree(),
       no_damage_message(), special_damage_message(), aux_attack(), aux_verb(),
       attacker_armour_tohit_penalty(0), attacker_shield_tohit_penalty(0),
-      defender_shield(NULL), miscast_level(-1), miscast_type(SPTYP_NONE),
-      miscast_target(NULL), fake_chaos_attack(false), simu(false),
+      defender_shield(nullptr), miscast_level(-1), miscast_type(SPTYP_NONE),
+      miscast_target(nullptr), fake_chaos_attack(false), simu(false),
       aux_source(""), kill_type(KILLED_BY_MONSTER)
 {
     // No effective code should execute, we'll call init_attack again from
@@ -284,7 +284,7 @@ int attack::calc_to_hit(bool random)
 
     // If no defender, we're calculating to-hit for debug-display
     // purposes, so don't drop down to defender code below
-    if (defender == NULL)
+    if (defender == nullptr)
         return mhit;
 
     if (!defender->visible_to(attacker))
@@ -577,7 +577,7 @@ bool attack::distortion_affects_defender()
 void attack::antimagic_affects_defender(int pow)
 {
     obvious_effect =
-        enchant_actor_with_flavour(defender, NULL, BEAM_DRAIN_MAGIC, pow);
+        enchant_actor_with_flavour(defender, nullptr, BEAM_DRAIN_MAGIC, pow);
 }
 
 void attack::pain_affects_defender()
@@ -993,7 +993,7 @@ void attack::do_miscast()
     if (miscast_level == -1)
         return;
 
-    ASSERT(miscast_target != NULL);
+    ASSERT(miscast_target != nullptr);
     ASSERT_RANGE(miscast_level, 0, 4);
     ASSERT(count_bits(miscast_type) == 1);
 
@@ -1106,13 +1106,8 @@ int attack::inflict_damage(int dam, beam_type flavour, bool clean)
         // gets the zombie. Too rare a case to care any more.
         defender->props["reaper"].get_int() = attacker->mid;
     }
-    if (defender->is_player())
-    {
-        ouch(dam, kill_type, responsible->mid, aux_source.c_str(),
-             you.can_see(attacker));
-        return dam;
-    }
-    return defender->hurt(responsible, dam, flavour, clean);
+    return defender->hurt(responsible, dam, flavour, kill_type,
+                          "", aux_source.c_str(), clean);
 }
 
 /* If debug, return formatted damage done
@@ -1238,7 +1233,7 @@ string attack::def_name(description_level_type desc)
  */
 string attack::wep_name(description_level_type desc, iflags_t ignre_flags)
 {
-    ASSERT(weapon != NULL);
+    ASSERT(weapon != nullptr);
 
     if (attacker->is_player())
         return weapon->name(desc, false, false, false, false, ignre_flags);
index fd2ded0..d3cfe05 100644 (file)
@@ -141,11 +141,11 @@ protected:
     // Determine if we're blocking (partially or entirely)
     virtual bool attack_shield_blocked(bool verbose);
     virtual bool attack_ignores_shield(bool verbose) = 0;
-    virtual bool apply_damage_brand(const char *what = NULL);
+    virtual bool apply_damage_brand(const char *what = nullptr);
     void calc_elemental_brand_damage(beam_type flavour,
                                      int res,
                                      const char *verb,
-                                     const char *what = NULL);
+                                     const char *what = nullptr);
 
     /* Weapon Effects */
     virtual bool check_unrand_effects() = 0;
index 7a06a45..8abfc27 100644 (file)
@@ -161,7 +161,7 @@ bool yred_slaves_abandon_you()
     for (radius_iterator ri(you.pos(), LOS_DEFAULT); ri; ++ri)
     {
         monster* mons = monster_at(*ri);
-        if (mons == NULL)
+        if (mons == nullptr)
             continue;
 
         if (is_yred_undead_slave(mons))
@@ -212,7 +212,7 @@ bool beogh_followers_abandon_you()
     for (radius_iterator ri(you.pos(), LOS_DEFAULT); ri; ++ri)
     {
         monster* mons = monster_at(*ri);
-        if (mons == NULL)
+        if (mons == nullptr)
             continue;
 
         // Note that orc high priests' summons are gifts of Beogh,
@@ -414,7 +414,7 @@ void gozag_set_bribe(monster* traitor)
         const monster* leader =
             traitor->props.exists("band_leader")
             ? monster_by_mid(traitor->props["band_leader"].get_int())
-            : NULL;
+            : nullptr;
 
         int cost = max(1, exper_value(traitor) / 20);
 
index bd54e9a..af09cf4 100644 (file)
@@ -78,7 +78,7 @@
 #define SAP_MAGIC_CHANCE() x_chance_in_y(7, 10)
 
 // Helper functions (some of these should probably be public).
-static void _ench_animation(int flavour, const monster* mon = NULL,
+static void _ench_animation(int flavour, const monster* mon = nullptr,
                             bool force = false);
 static beam_type _chaos_beam_flavour(bolt* beam);
 static string _beam_type_name(beam_type type);
@@ -141,7 +141,7 @@ kill_category bolt::whose_kill() const
 
 // A simple animated flash from Rupert Smith (expanded to be more
 // generic).
-static void _zap_animation(int colour, const monster* mon = NULL,
+static void _zap_animation(int colour, const monster* mon = nullptr,
                            bool force = false)
 {
     coord_def p = you.pos();
@@ -357,7 +357,7 @@ public:
 struct zap_info
 {
     zap_type ztype;
-    const char* name;           // NULL means handled specially
+    const char* name;           // nullptr means handled specially
     int power_cap;
     dam_deducer* damage;
     tohit_deducer* tohit;       // Enchantments have power modifier here
@@ -388,7 +388,7 @@ static const zap_info* _seek_zap(zap_type z_type)
 {
     ASSERT_RANGE(z_type, 0, NUM_ZAPS);
     if (zap_index[z_type] == -1)
-        return NULL;
+        return nullptr;
     else
         return &zap_data[zap_index[z_type]];
 }
@@ -405,7 +405,7 @@ void zappy(zap_type z_type, int power, bolt &pbolt)
     const zap_info* zinfo = _seek_zap(z_type);
 
     // None found?
-    if (zinfo == NULL)
+    if (zinfo == nullptr)
     {
 #ifdef DEBUG_DIAGNOSTICS
         mprf(MSGCH_ERROR, "Couldn't find zap type %d", z_type);
@@ -428,11 +428,13 @@ void zappy(zap_type z_type, int power, bolt &pbolt)
 
     ASSERT(zinfo->is_enchantment == pbolt.is_enchantment());
 
+    if (zinfo->is_enchantment && zinfo->tohit)
+        pbolt.ench_power = (*zinfo->tohit)(power);
+    else
+        pbolt.ench_power = power;
+
     if (zinfo->is_enchantment)
-    {
-        pbolt.ench_power = (zinfo->tohit ? (*zinfo->tohit)(power) : power);
         pbolt.hit = AUTOMATIC_HIT;
-    }
     else
     {
         pbolt.hit = (*zinfo->tohit)(power);
@@ -443,9 +445,6 @@ void zappy(zap_type z_type, int power, bolt &pbolt)
     if (zinfo->damage)
         pbolt.damage = (*zinfo->damage)(power);
 
-    if (z_type == ZAP_EXPLOSIVE_BOLT)
-        pbolt.ench_power = power;
-
     pbolt.origin_spell = zap_to_spell(z_type);
 
     if (z_type == ZAP_BREATHE_FIRE && you.species == SP_RED_DRACONIAN)
@@ -546,11 +545,11 @@ void bolt::initialise_fire()
     use_target_as_pos  = false;
     hit_count.clear();
 
-    if (special_explosion != NULL)
+    if (special_explosion != nullptr)
     {
         ASSERT(!is_explosion);
         ASSERT(special_explosion->is_explosion);
-        ASSERT(special_explosion->special_explosion == NULL);
+        ASSERT(special_explosion->special_explosion == nullptr);
         special_explosion->in_explosion_phase = false;
         special_explosion->use_target_as_pos  = false;
     }
@@ -1211,12 +1210,12 @@ void bolt::fire()
     if (is_tracer)
     {
         bolt boltcopy = *this;
-        if (special_explosion != NULL)
+        if (special_explosion != nullptr)
             boltcopy.special_explosion = new bolt(*special_explosion);
 
         do_fire();
 
-        if (special_explosion != NULL)
+        if (special_explosion != nullptr)
         {
             _undo_tracer(*special_explosion, *boltcopy.special_explosion);
             delete boltcopy.special_explosion;
@@ -1227,7 +1226,7 @@ void bolt::fire()
     else
         do_fire();
 
-    if (special_explosion != NULL)
+    if (special_explosion != nullptr)
     {
         seen           = seen  || special_explosion->seen;
         heard          = heard || special_explosion->heard;
@@ -1254,7 +1253,8 @@ void bolt::do_fire()
 #ifdef USE_TILE
     tile_beam = -1;
 
-    if (item && !is_tracer && flavour == BEAM_MISSILE)
+    if (item && !is_tracer && (flavour == BEAM_MISSILE
+                               || flavour == BEAM_VISUAL))
     {
         const coord_def diff = target - source;
         tile_beam = tileidx_item_throw(get_item_info(*item), diff.x, diff.y);
@@ -1840,8 +1840,8 @@ static bool _monster_resists_mass_enchantment(monster* mons,
 }
 
 // Enchants all monsters in player's sight.
-// If m_succumbed is non-NULL, will be set to the number of monsters that
-// were enchanted. If m_attempted is non-NULL, will be set to the number of
+// If m_succumbed is non-nullptr, will be set to the number of monsters that
+// were enchanted. If m_attempted is not nullptr, will be set to the number of
 // monsters that we tried to enchant.
 spret_type mass_enchantment(enchant_type wh_enchant, int pow, bool fail)
 {
@@ -1880,7 +1880,7 @@ spret_type mass_enchantment(enchant_type wh_enchant, int pow, bool fail)
             case ENCH_FEAR:      msg = " looks frightened!";      break;
             case ENCH_CONFUSION: msg = " looks rather confused."; break;
             case ENCH_CHARM:     msg = " submits to your will.";  break;
-            default:             msg = NULL;                      break;
+            default:             msg = nullptr;                   break;
             }
             if (msg && simple_monster_message(*mi, msg))
                 did_msg = true;
@@ -2369,10 +2369,8 @@ static void _malign_offering_effect(actor* victim, const actor* agent, int damag
     coord_def c = victim->pos();
 
     mprf("%s life force is offered up.", victim->name(DESC_ITS).c_str());
-    if (victim->is_player())
-        ouch(damage, KILLED_BY_BEAM, agent->mid, "by a malign offering");
-    else
-        damage = victim->hurt(agent, damage, BEAM_NEG);
+    damage = victim->hurt(agent, damage, BEAM_NEG, KILLED_BY_BEAM,
+                          "", "by a malign offering");
 
     // Actors that had LOS to the victim (blocked by glass, clouds, etc),
     // even if they couldn't actually see each other because of blindness
@@ -2623,7 +2621,7 @@ bool bolt::stop_at_target() const
 
 void bolt::drop_object()
 {
-    ASSERT(item != NULL);
+    ASSERT(item != nullptr);
     ASSERT(item->defined());
 
     // Conditions: beam is missile and not tracer.
@@ -2708,7 +2706,7 @@ void bolt::affect_ground()
 
             if (create_monster(mgen_data(MONS_BALLISTOMYCETE,
                                          beh,
-                                         NULL,
+                                         nullptr,
                                          0,
                                          0,
                                          pos(),
@@ -2903,7 +2901,7 @@ void bolt::affect_place_explosion_clouds()
                          p, MHITNOT, 0, god);
 
             // Spell-summoned monsters need to have a live summoner.
-            if (summ == NULL || !summ->alive())
+            if (summ == nullptr || !summ->alive())
             {
                 if (!source_name.empty())
                     mg.non_actor_summoner = source_name;
@@ -2919,7 +2917,7 @@ void bolt::affect_place_explosion_clouds()
 // A little helper function to handle the calling of ouch()...
 void bolt::internal_ouch(int dam)
 {
-    monster* monst = NULL;
+    monster* monst = nullptr;
     monst = monster_by_mid(source_id);
 
     const char *what = aux_source.empty() ? name.c_str() : aux_source.c_str();
@@ -2932,8 +2930,8 @@ void bolt::internal_ouch(int dam)
         && !monst->mname.empty())
     {
         ouch(dam, KILLED_BY_DIVINE_WRATH, MID_NOBODY,
-             aux_source.empty() ? NULL : aux_source.c_str(), true,
-             source_name.empty() ? NULL : source_name.c_str());
+             aux_source.empty() ? nullptr : aux_source.c_str(), true,
+             source_name.empty() ? nullptr : source_name.c_str());
     }
     else if (monst && (monst->type == MONS_GIANT_SPORE
                        || monst->type == MONS_BALL_LIGHTNING
@@ -2944,12 +2942,12 @@ void bolt::internal_ouch(int dam)
     {
         ouch(dam, KILLED_BY_SPORE, source_id,
              aux_source.c_str(), true,
-             source_name.empty() ? NULL : source_name.c_str());
+             source_name.empty() ? nullptr : source_name.c_str());
     }
     else if (flavour == BEAM_DISINTEGRATION || flavour == BEAM_DEVASTATION)
     {
         ouch(dam, KILLED_BY_DISINT, source_id, what, true,
-             source_name.empty() ? NULL : source_name.c_str());
+             source_name.empty() ? nullptr : source_name.c_str());
     }
     else if (YOU_KILL(thrower) && aux_source.empty())
     {
@@ -2968,7 +2966,7 @@ void bolt::internal_ouch(int dam)
     else if (MON_KILL(thrower) || aux_source == "exploding inner flame")
         ouch(dam, KILLED_BY_BEAM, source_id,
              aux_source.c_str(), true,
-             source_name.empty() ? NULL : source_name.c_str());
+             source_name.empty() ? nullptr : source_name.c_str());
     else // KILL_MISC || (YOU_KILL && aux_source)
         ouch(dam, KILLED_BY_WILD_MAGIC, source_id, aux_source.c_str());
 }
@@ -3449,7 +3447,7 @@ void bolt::affect_player_enchantment(bool resistible)
     {
     case BEAM_HIBERNATION:
     case BEAM_SLEEP:
-        you.put_to_sleep(NULL, ench_power, flavour == BEAM_HIBERNATION);
+        you.put_to_sleep(nullptr, ench_power, flavour == BEAM_HIBERNATION);
         break;
 
     case BEAM_CORONA:
@@ -4581,8 +4579,14 @@ void bolt::monster_post_hit(monster* mon, int dmg)
 void bolt::beam_hits_actor(actor *act)
 {
     const coord_def oldpos(act->pos());
+    coord_def newpos(act->pos());
+
+    const int distance =
+        (origin_spell == SPELL_FORCE_LANCE)
+            ? 1 + div_rand_round(ench_power, 40) :
+        (origin_spell == SPELL_CHILLING_BREATH) ? 2 : 1;
 
-    if (knockback_actor(act))
+    if (knockback_actor(act, distance, newpos))
     {
         if (you.can_see(act))
         {
@@ -4591,7 +4595,6 @@ void bolt::beam_hits_actor(actor *act)
                 mprf("%s %s blown backwards by the freezing wind.",
                      act->name(DESC_THE).c_str(),
                      act->conj_verb("are").c_str());
-                knockback_actor(act);
             }
             else
             {
@@ -4602,9 +4605,8 @@ void bolt::beam_hits_actor(actor *act)
             }
         }
 
-        // Force lance can knockback up to two spaces
-        if (origin_spell == SPELL_FORCE_LANCE && coinflip())
-            knockback_actor(act);
+        if (act->pos() != newpos)
+            act->collide(newpos, agent(), ench_power);
 
         // Stun the monster briefly so that it doesn't look as though it wasn't
         // knocked back at all
@@ -4653,6 +4655,8 @@ void bolt::hit_shield(actor* blocker) const
                 you.props[MELT_SHIELD_KEY] = true;
         }
     }
+    if (blocker->is_player())
+        you.maybe_degrade_bone_armour();
 }
 
 // Return true if the block succeeded (including reflections.)
@@ -4972,7 +4976,7 @@ void bolt::affect_monster(monster* mon)
             bleed_onto_floor(mon->pos(), mon->type, blood, true);
         }
         // Now hurt monster.
-        mon->hurt(agent(), final, flavour, false);
+        mon->hurt(agent(), final, flavour, KILLED_BY_BEAM, "", "", false);
     }
 
     if (mon->alive())
@@ -5030,7 +5034,7 @@ bool bolt::ignores_monster(const monster* mon) const
     if (flavour == BEAM_DIGGING)
         return true;
 
-    // The targetters might call us with NULL in the event of a remembered
+    // The targetters might call us with nullptr in the event of a remembered
     // monster that is no longer there. Treat it as opaque.
     if (!mon)
         return false;
@@ -5165,6 +5169,11 @@ bool ench_flavour_affects_monster(beam_type flavour, const monster* mon,
         rc = !(mon->is_summoned() || mon->has_ench(ENCH_INNER_FLAME));
         break;
 
+    case BEAM_CORONA:
+        rc = !mon->glows_naturally()
+             && !mons_class_flag(mon->type, M_SHADOW);
+        break;
+
     default:
         break;
     }
@@ -5760,37 +5769,50 @@ int bolt::range_used_on_hit() const
 // Checks whether the beam knocks back the supplied actor. The actor
 // should have already failed their EV check, so the save is entirely
 // body-mass-based.
-bool bolt::knockback_actor(actor *act)
+bool bolt::knockback_actor(actor *act, int distance, coord_def &newpos)
 {
     // We can't do knockback if the beam starts and ends on the same space
     if (source == act->pos())
         return false;
 
+    if (act->is_stationary())
+        return false;
+
+    const int roll = origin_spell == SPELL_FORCE_LANCE
+                     ? 1000 + 40 * ench_power
+                     : 2500;
+    const int weight = act->body_weight() / (act->airborne() ? 2 : 1);
+
     ASSERT(ray.pos() == act->pos());
 
-    const coord_def oldpos(ray.pos());
-    const ray_def ray_copy(ray);
-    ray.advance();
-
-    const coord_def newpos(ray.pos());
-    if (newpos == oldpos
-        || actor_at(newpos)
-        || act->is_stationary()
-        || cell_is_solid(newpos)
-        || !act->can_pass_through(newpos)
-        || !act->is_habitable(newpos)
-        // Save is based on target's body weight.
-        || random2(2500) < act->body_weight())
+    const coord_def initial_pos(ray.pos());
+    for (int dist_travelled = 0; dist_travelled < distance; ++dist_travelled)
     {
-        ray = ray_copy;
-        return false;
-    }
+        // Save is based on target's body weight.
+        if (random2(roll) < weight)
+            continue;
+
+        const ray_def ray_copy(ray);
 
-    act->move_to_pos(newpos);
+        ray.advance();
+
+        newpos = ray.pos();
+        if (newpos == ray_copy.pos()
+            || cell_is_solid(newpos)
+            || actor_at(newpos)
+            || !act->can_pass_through(newpos)
+            || !act->is_habitable(newpos))
+        {
+            ray = ray_copy;
+            return newpos != initial_pos;
+        }
+
+        act->move_to_pos(newpos);
+    }
 
     // Knockback cannot ever kill the actor directly - caller must do
     // apply_location_effects after messaging.
-    return true;
+    return ray.pos() != initial_pos;
 }
 
 // Takes a bolt and refines it for use in the explosion function.
@@ -5800,8 +5822,8 @@ void bolt::refine_for_explosion()
 {
     ASSERT(!special_explosion);
 
-    const char *seeMsg  = NULL;
-    const char *hearMsg = NULL;
+    const char *seeMsg  = nullptr;
+    const char *hearMsg = nullptr;
 
     if (ex_size == 0)
         ex_size = 1;
@@ -5813,7 +5835,7 @@ void bolt::refine_for_explosion()
     // tmp needed so that what c_str() points to doesn't go out of scope
     // before the function ends.
     string tmp;
-    if (item != NULL)
+    if (item != nullptr)
     {
         tmp  = "The " + item->name(DESC_PLAIN, false, false, false)
                + " explodes!";
@@ -5890,7 +5912,7 @@ void bolt::refine_for_explosion()
         ex_size = 1;
     }
 
-    if (seeMsg == NULL)
+    if (seeMsg == nullptr)
     {
         seeMsg  = "The beam explodes into a cloud of software bugs!";
         hearMsg = "You hear the sound of one hand!";
@@ -6299,7 +6321,7 @@ bool bolt::nice_to(const monster* mon) const
 // (extended from setup_mons_cast() and zapping() which act as limited ones).
 bolt::bolt() : origin_spell(SPELL_NO_SPELL),
                range(-2), glyph('*'), colour(BLACK), flavour(BEAM_MAGIC),
-               real_flavour(BEAM_MAGIC), drop_item(false), item(NULL),
+               real_flavour(BEAM_MAGIC), drop_item(false), item(nullptr),
                source(), target(), damage(0, 0), ench_power(0), hit(0),
                thrower(KILL_MISC), ex_size(0), source_id(MID_NOBODY),
                source_name(), name(), short_name(), hit_verb(),
@@ -6308,7 +6330,7 @@ bolt::bolt() : origin_spell(SPELL_NO_SPELL),
                affects_nothing(false), affects_items(true), effect_known(true),
                effect_wanton(false),
                draw_delay(15), explode_delay(50),
-               special_explosion(NULL), was_missile(false),
+               special_explosion(nullptr), was_missile(false),
                animate(Options.use_animations & UA_BEAM),
                ac_rule(AC_NORMAL),
 #ifdef DEBUG_DIAGNOSTICS
@@ -6379,7 +6401,7 @@ void bolt::setup_retrace()
 
 void bolt::set_agent(actor *actor)
 {
-    // NULL actor is fine by us.
+    // nullptr actor is fine by us.
     if (!actor)
         return;
 
@@ -6420,7 +6442,7 @@ string bolt::get_short_name() const
     if (!short_name.empty())
         return short_name;
 
-    if (item != NULL && item->defined())
+    if (item != nullptr && item->defined())
     {
         return item->name(DESC_A, false, false, false, false,
                           ISFLAG_IDENT_MASK | ISFLAG_COSMETIC_MASK);
@@ -6556,7 +6578,7 @@ string bolt::get_source_name() const
  * The bolts that knockback flying actors or actors only when damage
  * is dealt will return when.
  *
- * @param act The target actor. If not-NULL, check if the actor is flying for
+ * @param act The target actor. If not-nullptr, check if the actor is flying for
  *            bolts that knockback flying actors.
  * @param dam The damage dealt. If non-negative, check that dam > 0 for bolts
  *             like force bolt that only push back upon damage.
index c29d3cf..54a4632 100644 (file)
@@ -177,7 +177,7 @@ public:
 
     // Return whether any affected cell was seen.
     bool explode(bool show_more = true, bool hole_in_the_middle = false);
-    bool knockback_actor(actor *actor);
+    bool knockback_actor(actor *actor, int distance, coord_def &newpos);
 
     bool visible() const;
 
@@ -282,7 +282,7 @@ public:
     void determine_affected_cells(explosion_map& m, const coord_def& delta,
                                   int count, int r,
                                   bool stop_at_statues, bool stop_at_walls);
-    bool can_knockback(const actor *act = NULL, int dam = -1) const;
+    bool can_knockback(const actor *act = nullptr, int dam = -1) const;
 };
 
 int mons_adjust_flavoured(monster* mons, bolt &pbolt, int hurted,
@@ -312,7 +312,7 @@ void fire_tracer(const monster* mons, bolt &pbolt,
 bool imb_can_splash(coord_def origin, coord_def center,
                     vector<coord_def> path_taken, coord_def target);
 spret_type zapping(zap_type ztype, int power, bolt &pbolt,
-                   bool needs_tracer = false, const char* msg = NULL,
+                   bool needs_tracer = false, const char* msg = nullptr,
                    bool fail = false);
 bool player_tracer(zap_type ztype, int power, bolt &pbolt, int range = 0);
 
index 67164c4..b718b41 100644 (file)
@@ -87,7 +87,7 @@ monster* player::get_beholder(const coord_def &target) const
         if (olddist < newdist)
             return mon;
     }
-    return NULL;
+    return nullptr;
 }
 
 monster* player::get_any_beholder() const
@@ -95,7 +95,7 @@ monster* player::get_any_beholder() const
     if (!beholders.empty())
         return monster_by_mid(beholders[0]);
     else
-        return NULL;
+        return nullptr;
 }
 
 // Removes a monster from the list of beholders if present.
index 1e82fa0..90f7c6c 100644 (file)
@@ -12,7 +12,7 @@ void bleed_onto_floor(const coord_def& where, monster_type mon, int damage,
                       const bool old_blood = false);
 void blood_spray(const coord_def& where, monster_type mon, int level);
 void generate_random_blood_spatter_on_level(
-                                            const map_bitmask *susceptible_area = NULL);
+                                            const map_bitmask *susceptible_area = nullptr);
 
 // Set FPROP_BLOODY after checking bleedability.
 bool maybe_bloodify_square(const coord_def& where);
index 875335b..75ee3e3 100644 (file)
@@ -256,12 +256,12 @@ static spell_type spellbook_template_array[][SPELLBOOK_SIZE] =
 },
 
 {   // Book of the Warp
+    SPELL_RECALL,
     SPELL_CONTROL_TELEPORT,
+    SPELL_FORCE_LANCE,
     SPELL_PHASE_SHIFT,
     SPELL_WARP_BRAND,
-    SPELL_DISPERSAL,
-    SPELL_CONTROLLED_BLINK,
-    SPELL_DISJUNCTION,
+    SPELL_SUMMON_FOREST,
     SPELL_NO_SPELL,
     SPELL_NO_SPELL,
 },
@@ -281,7 +281,7 @@ static spell_type spellbook_template_array[][SPELLBOOK_SIZE] =
     SPELL_RECALL,
     SPELL_ANIMATE_DEAD,
     SPELL_CONTROL_UNDEAD,
-    SPELL_TWISTED_RESURRECTION,
+    SPELL_BONE_ARMOUR,
     SPELL_DEATH_CHANNEL,
     SPELL_SIMULACRUM,
     SPELL_NO_SPELL,
@@ -337,12 +337,12 @@ static spell_type spellbook_template_array[][SPELLBOOK_SIZE] =
 {   // Book of Wizardry
     SPELL_AGONY,
     SPELL_FORCE_LANCE,
-    SPELL_DISPERSAL,
     SPELL_HASTE,
     SPELL_INVISIBILITY,
     SPELL_SPELLFORGED_SERVITOR,
     SPELL_NO_SPELL,
     SPELL_NO_SPELL,
+    SPELL_NO_SPELL,
 },
 
 {   // Book of Power
@@ -378,18 +378,16 @@ static spell_type spellbook_template_array[][SPELLBOOK_SIZE] =
     SPELL_NO_SPELL,
 },
 
-#if TAG_MAJOR_VERSION == 34
-{   // Book of Stalking
-    SPELL_NO_SPELL,
-    SPELL_NO_SPELL,
-    SPELL_NO_SPELL,
-    SPELL_NO_SPELL,
-    SPELL_NO_SPELL,
+{   // Plane Papyrus
+    SPELL_DISPERSAL,
+    SPELL_CONTROLLED_BLINK,
+    SPELL_MALIGN_GATEWAY,
+    SPELL_DISJUNCTION,
+    SPELL_SINGULARITY,
     SPELL_NO_SPELL,
     SPELL_NO_SPELL,
     SPELL_NO_SPELL,
 },
-#endif
 
 {   // Book of Debilitation
     SPELL_CORONA,
index b36b5c1..7efefb0 100644 (file)
@@ -14,7 +14,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       NUM_FEATURES, DNGN_EXIT_DUNGEON,
       "Dungeon", "the Dungeon", "D",
-      NULL,
+      nullptr,
       LIGHTGREY, BROWN,
       'D', false, 0 },
 
@@ -22,7 +22,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_TEMPLE, DNGN_RETURN_FROM_TEMPLE,
       "Temple", "the Ecumenical Temple", "Temple",
-      NULL,
+      nullptr,
       LIGHTGREY, BROWN,
       'T', false, 0 },
 
@@ -30,7 +30,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_ORC, DNGN_RETURN_FROM_ORC,
       "Orcish Mines", "the Orcish Mines", "Orc",
-      NULL,
+      nullptr,
       BROWN, BROWN,
       'O', false, 4 },
 
@@ -38,7 +38,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_ELF, DNGN_RETURN_FROM_ELF,
       "Elven Halls", "the Elven Halls", "Elf",
-      NULL,
+      nullptr,
       WHITE, ETC_ELVEN_BRICK,
       'E', true, 0 },
 
@@ -47,7 +47,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_DWARF, DNGN_RETURN_FROM_DWARF,
       "Dwarven Hall", "the Dwarven Hall", "Dwarf",
-      NULL,
+      nullptr,
       BROWN, BROWN,
       'K', false, 0 },
 #endif
@@ -56,7 +56,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_LAIR, DNGN_RETURN_FROM_LAIR,
       "Lair", "the Lair of Beasts", "Lair",
-      NULL,
+      nullptr,
       GREEN, BROWN,
       'L', false, 4 },
 
@@ -64,7 +64,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_SWAMP, DNGN_RETURN_FROM_SWAMP,
       "Swamp", "the Swamp", "Swamp",
-      NULL,
+      nullptr,
       BROWN, BROWN,
       'S', true, 0 },
 
@@ -72,7 +72,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_ISLANDED, 0,
       DNGN_ENTER_SHOALS, DNGN_RETURN_FROM_SHOALS,
       "Shoals", "the Shoals", "Shoals",
-      NULL,
+      nullptr,
       BROWN, BROWN,
       'A', true, 3 },
 
@@ -80,7 +80,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_SNAKE, DNGN_RETURN_FROM_SNAKE,
       "Snake Pit", "the Snake Pit", "Snake",
-      NULL,
+      nullptr,
       LIGHTGREEN, YELLOW,
       'P', true, 0 },
 
@@ -88,7 +88,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_SPIDER, DNGN_RETURN_FROM_SPIDER,
       "Spider Nest", "the Spider Nest", "Spider",
-      NULL,
+      nullptr,
       BROWN, YELLOW,
       'N', true, 0 },
 
@@ -96,7 +96,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_SLIME, DNGN_RETURN_FROM_SLIME,
       "Slime Pits", "the Pits of Slime", "Slime",
-      NULL,
+      nullptr,
       GREEN, BROWN,
       'M', true, -5 },
 
@@ -104,7 +104,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_VAULTS, DNGN_RETURN_FROM_VAULTS,
       "Vaults", "the Vaults", "Vaults",
-      NULL,
+      nullptr,
       LIGHTGREY, BROWN,
       'V', true, 0 },
 #if TAG_MAJOR_VERSION == 34
@@ -112,7 +112,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_BLADE, DNGN_RETURN_FROM_BLADE,
       "Hall of Blades", "the Hall of Blades", "Blade",
-      NULL,
+      nullptr,
       LIGHTGREY, BROWN,
       'B', false, -7 },
 #endif
@@ -121,7 +121,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_CRYPT, DNGN_RETURN_FROM_CRYPT,
       "Crypt", "the Crypt", "Crypt",
-      NULL,
+      nullptr,
       LIGHTGREY, BROWN,
       'C', true, -3 },
 
@@ -129,7 +129,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_ISLANDED, LFLAG_NO_TELE_CONTROL,
       DNGN_ENTER_TOMB, DNGN_RETURN_FROM_TOMB,
       "Tomb", "the Tomb of the Ancients", "Tomb",
-      NULL,
+      nullptr,
       BROWN, BROWN,
       'W', true, -10 },
 
@@ -145,7 +145,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_ISLANDED | BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_DIS, DNGN_ENTER_HELL,
       "Dis", "the Iron City of Dis", "Dis",
-      NULL,
+      nullptr,
       CYAN, BROWN,
       'I', true, 0 },
 
@@ -153,7 +153,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_ISLANDED | BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_GEHENNA, DNGN_ENTER_HELL,
       "Gehenna", "Gehenna", "Geh",
-      NULL,
+      nullptr,
       BROWN, RED,
       'G', true, 0 },
 
@@ -161,7 +161,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_ISLANDED | BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_COCYTUS, DNGN_ENTER_HELL,
       "Cocytus", "Cocytus", "Coc",
-      NULL,
+      nullptr,
       LIGHTBLUE, LIGHTCYAN,
       'X', true, 0 },
 
@@ -169,7 +169,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_ISLANDED | BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_TARTARUS, DNGN_ENTER_HELL,
       "Tartarus", "Tartarus", "Tar",
-      NULL,
+      nullptr,
       MAGENTA, MAGENTA,
       'Y', true, 0 },
 
@@ -177,7 +177,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_ZOT, DNGN_RETURN_FROM_ZOT,
       "Zot", "the Realm of Zot", "Zot",
-      NULL,
+      nullptr,
       BLACK, BLACK, // set per-map
       'Z', true, 0 },
 #if TAG_MAJOR_VERSION == 34
@@ -185,7 +185,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_FOREST, DNGN_RETURN_FROM_FOREST,
       "Forest", "the Enchanted Forest", "Forest",
-      NULL,
+      nullptr,
       BROWN, BROWN,
       'F', true, 0 },
 #endif
@@ -194,7 +194,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_NO_XLEV_TRAVEL, LFLAG_NO_TELE_CONTROL | LFLAG_NO_MAP,
       DNGN_ENTER_ABYSS, DNGN_EXIT_ABYSS,
       "Abyss", "the Abyss", "Abyss",
-      NULL,
+      nullptr,
       BLACK, BLACK, // set specially
       'J', false, 0 },
 
@@ -239,7 +239,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_NO_XLEV_TRAVEL | BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_TROVE, DNGN_EXIT_TROVE,
       "Trove", "a treasure trove", "Trove",
-      NULL,
+      nullptr,
       DARKGREY, BLUE,
       '2', false, 0 },
 
@@ -247,7 +247,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_NO_XLEV_TRAVEL | BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_SEWER, DNGN_EXIT_SEWER,
       "Sewer", "a sewer", "Sewer",
-      NULL,
+      nullptr,
       LIGHTGREY, BLUE,
       '3', false, 0 },
 
@@ -255,7 +255,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_NO_XLEV_TRAVEL | BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_OSSUARY, DNGN_EXIT_OSSUARY,
       "Ossuary", "an ossuary", "Ossuary",
-      NULL,
+      nullptr,
       WHITE, YELLOW,
       '4', false, 0 },
 
@@ -263,7 +263,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_NO_XLEV_TRAVEL | BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_BAILEY, DNGN_EXIT_BAILEY,
       "Bailey", "a bailey", "Bailey",
-      NULL,
+      nullptr,
       WHITE, LIGHTRED,
       '5', false, 0 },
 
@@ -271,7 +271,7 @@ const Branch branches[NUM_BRANCHES] =
           BFLAG_NO_XLEV_TRAVEL | BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_ICE_CAVE, DNGN_EXIT_ICE_CAVE,
       "Ice Cave", "an ice cave", "IceCv",
-      NULL,
+      nullptr,
       BLUE, WHITE,
       '6', false, 0 },
 
@@ -279,7 +279,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_NO_XLEV_TRAVEL | BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_VOLCANO, DNGN_EXIT_VOLCANO,
       "Volcano", "a volcano", "Volcano",
-      NULL,
+      nullptr,
       RED, RED,
       '7', false, 0 },
 
@@ -287,7 +287,7 @@ const Branch branches[NUM_BRANCHES] =
       BFLAG_NO_XLEV_TRAVEL | BFLAG_NO_ITEMS, 0,
       DNGN_ENTER_WIZLAB, DNGN_EXIT_WIZLAB,
       "Wizlab", "a wizard's laboratory", "WizLab",
-      NULL,
+      nullptr,
       LIGHTGREY, BROWN, // set per-map
       '8', false, 0 },
 
@@ -295,7 +295,7 @@ const Branch branches[NUM_BRANCHES] =
       0, 0,
       DNGN_ENTER_DEPTHS, DNGN_RETURN_FROM_DEPTHS,
       "Depths", "the Depths", "Depths",
-      NULL,
+      nullptr,
       LIGHTGREY, BROWN,
       'U', false, 0 },
 };
index 5379bd0..0e4df84 100644 (file)
@@ -43,7 +43,8 @@ struct Branch
     int ambient_noise;           // affects noise loudness and player stealth
 };
 
-class branch_iterator {
+class branch_iterator
+{
 public:
     branch_iterator();
 
index 558b954..8f237ff 100644 (file)
@@ -110,6 +110,27 @@ static string _butcher_menu_title(const Menu *menu, const string &oldt)
 }
 #endif
 
+static int _corpse_quality(const item_def &item, bool bottle_blood)
+{
+    const corpse_effect_type ce = determine_chunk_effect(item);
+    // Being almost rotten away has 480 badness.
+    int badness = 3 * item.freshness;
+    if (ce == CE_POISONOUS)
+        badness += 600;
+    else if (ce == CE_MUTAGEN)
+        badness += 1000;
+    else if (ce == CE_ROT)
+        badness += 1000;
+
+    // Bottleable corpses first, unless forbidden
+    if (bottle_blood && !can_bottle_blood_from_corpse(item.mon_type))
+        badness += 4000;
+
+    if (is_forbidden_food(item))
+        badness += 10000;
+    return -badness;
+}
+
 /**
  * Attempt to butcher a corpse.
  *
@@ -125,62 +146,52 @@ bool butchery(int which_corpse)
         return false;
     }
 
-    // First determine how many things there are to butcher.
-    int num_corpses = 0;
+    // First determine which things there are to butcher.
     int corpse_id   = -1;
-    int best_badness = INT_MAX;
     const bool prechosen  = (which_corpse != -1);
     const bool bottle_blood = you.species == SP_VAMPIRE;
+    typedef pair<item_def *, int> corpse_quality;
+    vector<corpse_quality> corpses;
+
     for (stack_iterator si(you.pos(), true); si; ++si)
     {
-        // we only care about corpses, of course.
         if (si->base_type != OBJ_CORPSES || si->sub_type != CORPSE_BODY)
             continue;
 
-        // Return pre-chosen corpse if it exists.
+        // If the pre-chosen corpse exists, pretend it was the only one.
         if (prechosen && si->index() == which_corpse)
         {
+            corpses = { { &*si, 0 } };
             corpse_id = si->index();
-            num_corpses = 1;
             break;
         }
 
-        const corpse_effect_type ce = determine_chunk_effect(*si);
-        // Being almost rotten away has 480 badness.
-        int badness = 3 * si->freshness;
-        if (ce == CE_POISONOUS)
-            badness += 600;
-        else if (ce == CE_MUTAGEN)
-            badness += 1000;
-        else if (ce == CE_ROT)
-            badness += 1000;
-
-        if (is_forbidden_food(*si))
-            badness += 10000;
-
-        if (badness < best_badness)
-            corpse_id = si->index(), best_badness = badness;
-        num_corpses++;
+        corpses.emplace_back(&*si, _corpse_quality(*si, bottle_blood));
     }
 
-    if (num_corpses == 0)
+    if (corpses.size() == 0)
     {
         mprf("There isn't anything to %s here.",
-             bottle_blood ? "bottle" : "butcher");
+             bottle_blood ? "bottle or butcher" : "butcher");
         return false;
     }
 
+    stable_sort(begin(corpses), end(corpses), greater_second<corpse_quality>());
+    // If we didn't select a pre-chosen corpse, pick the best one.
+    if (corpse_id < 0)
+        corpse_id = corpses[0].first->index();
+
     // Butcher pre-chosen corpse, if found, or if there is only one corpse.
     bool success = false;
     if (prechosen && corpse_id == which_corpse
-        || num_corpses == 1 && Options.confirm_butcher != CONFIRM_ALWAYS
+        || corpses.size() == 1 && Options.confirm_butcher != CONFIRM_ALWAYS
         || Options.confirm_butcher == CONFIRM_NEVER)
     {
         if (Options.confirm_butcher == CONFIRM_NEVER
             && !_should_butcher(mitm[corpse_id], bottle_blood))
         {
             mprf("There isn't anything suitable to %s here.",
-                 bottle_blood ? "bottle" : "butcher");
+                 bottle_blood ? "bottle or butcher" : "butcher");
             return false;
         }
 
@@ -193,13 +204,12 @@ bool butchery(int which_corpse)
     bool first_corpse  = true;
 #ifdef TOUCH_UI
     vector<const item_def*> meat;
-    for (stack_iterator si(you.pos(), true); si; ++si)
-        if (si->base_type == OBJ_CORPSES && si->sub_type == CORPSE_BODY)
-            meat.push_back(& (*si));
+    for (const auto &entry : corpses)
+        meat.push_back(entry.first);
 
     corpse_id = -1;
     vector<SelItem> selected =
-        select_items(meat, bottle_blood ? "Choose a corpse to bottle"
+        select_items(meat, bottle_blood ? "Choose a corpse to bottle or butcher"
                                         : "Choose a corpse to butcher",
                      false, MT_ANY, _butcher_menu_title);
     redraw_screen();
@@ -216,31 +226,30 @@ bool butchery(int which_corpse)
 #else
     int keyin;
     bool repeat_prompt = false;
-    for (stack_iterator si(you.pos(), true); si; ++si)
+    for (auto &entry : corpses)
     {
-        if (si->base_type != OBJ_CORPSES || si->sub_type != CORPSE_BODY)
-            continue;
+        item_def * const it = entry.first;
 
         if (butcher_all)
-            corpse_id = si->index();
+            corpse_id = it->index();
         else
         {
             corpse_id = -1;
 
-            string corpse_name = si->name(DESC_A);
+            string corpse_name = it->name(DESC_A);
 
             // We don't need to check for undead because
             // * Mummies can't eat.
             // * Ghouls relish the bad things.
             // * Vampires won't bottle bad corpses.
             if (you.undead_state() == US_ALIVE)
-                corpse_name = get_menu_colour_prefix_tags(*si, DESC_A);
+                corpse_name = get_menu_colour_prefix_tags(*it, DESC_A);
 
             // Shall we butcher this corpse?
             do
             {
                 const bool can_bottle =
-                    can_bottle_blood_from_corpse(si->mon_type);
+                    can_bottle_blood_from_corpse(it->mon_type);
                 mprf(MSGCH_PROMPT, "%s %s? [(y)es/(c)hop/(n)o/(a)ll/(q)uit/?]",
                      can_bottle ? "Bottle" : "Butcher",
                      corpse_name.c_str());
@@ -253,7 +262,7 @@ bool butchery(int which_corpse)
                 case 'c':
                 case 'd':
                 case 'a':
-                    corpse_id = si->index();
+                    corpse_id = it->index();
 
                     if (keyin == 'a')
                         butcher_all = true;
index eaae09e..95cbf45 100644 (file)
@@ -102,7 +102,7 @@ struct dump_params
     const scorefile_entry *se;
 
     dump_params(string &_text, const string &sec = "",
-                bool id = false, const scorefile_entry *s = NULL)
+                bool id = false, const scorefile_entry *s = nullptr)
         : text(_text), section(sec), full_id(id), se(s)
     {
     }
@@ -141,9 +141,9 @@ static dump_section_handler dump_handlers[] =
     { "-",              _sdump_separator     },
 
 #ifdef CLUA_BINDINGS
-    { NULL,             _sdump_lua           }
+    { nullptr,          _sdump_lua           }
 #else
-    { NULL,             NULL                }
+    { nullptr,          nullptr              }
 #endif
 };
 
@@ -1410,7 +1410,7 @@ static bool _write_dump(const string &fname, dump_params &par, bool quiet)
 
     dprf("File name: %s", file_name.c_str());
 
-    if (handle != NULL)
+    if (handle != nullptr)
     {
         fputs(OUTS(par.text), handle);
         fclose(handle);
@@ -1532,7 +1532,7 @@ static bool _dgl_unknown_timestamp_file(const string &filename)
     return false;
 }
 
-// Returns a filehandle to use to write turn timestamps, NULL if
+// Returns a filehandle to use to write turn timestamps, nullptr if
 // timestamps should not be written.
 static FILE *_dgl_timestamp_filehandle()
 {
@@ -1602,7 +1602,7 @@ static void _dgl_record_timestamp(int turn)
 {
     if (turn && turn < TIMESTAMP_TURN_MAX && !(turn % TIMESTAMP_TURN_INTERVAL))
     {
-        const time_t now = time(NULL);
+        const time_t now = time(nullptr);
         const unsigned long offset =
             (VERSION_SIZE +
              (turn / TIMESTAMP_TURN_INTERVAL - 1) * TIMESTAMP_SIZE);
index 02f1193..ef80c27 100644 (file)
@@ -26,7 +26,7 @@ enum item_origin_dump_selector
 class scorefile_entry;
 string morgue_directory();
 bool dump_char(const string &fname, bool quiet = false, bool full_id = false,
-               const scorefile_entry *se = NULL);
+               const scorefile_entry *se = nullptr);
 void dump_map(const char* fname, bool debug = false, bool dist = false);
 void dump_map(FILE *fp, bool debug = false, bool dist = false);
 void display_notes();
index e9d4d25..7046196 100644 (file)
@@ -213,7 +213,7 @@ void input_history::new_input(const string &s)
 const string *input_history::prev()
 {
     if (history.empty())
-        return NULL;
+        return nullptr;
 
     if (pos == history.begin())
         pos = history.end();
@@ -224,7 +224,7 @@ const string *input_history::prev()
 const string *input_history::next()
 {
     if (history.empty())
-        return NULL;
+        return nullptr;
 
     if (pos == history.end() || ++pos == history.end())
         pos = history.begin();
@@ -247,9 +247,9 @@ void input_history::clear()
 // line_reader
 
 line_reader::line_reader(char *buf, size_t sz, int wrap)
-    : buffer(buf), bufsz(sz), history(NULL), region(GOTO_CRT),
-      start(coord_def(0,0)), keyfn(NULL), wrapcol(wrap),
-      cur(NULL), length(0), pos(-1)
+    : buffer(buf), bufsz(sz), history(nullptr), region(GOTO_CRT),
+      start(coord_def(0,0)), keyfn(nullptr), wrapcol(wrap),
+      cur(nullptr), length(0), pos(-1)
 {
 }
 
index 13a8ccb..4dec616 100644 (file)
@@ -55,8 +55,8 @@ void nowrap_eol_cprintf(PRINTF(0, ));
 // pressed Escape
 int cancellable_get_line(char *buf,
                          int len,
-                         input_history *mh = NULL,
-                         int (*keyproc)(int &c) = NULL,
+                         input_history *mh = nullptr,
+                         int (*keyproc)(int &c) = nullptr,
                          const string &fill = "",
                          const string &tag = "");
 
index a9024ba..5ef8cfc 100644 (file)
@@ -16,7 +16,7 @@
 #include "coordit.h"
 #include "dungeon.h"
 #include "godconduct.h"
-#include "losglobal.h"
+#include "los.h"
 #include "mapmark.h"
 #include "melee_attack.h"
 #include "message.h"
@@ -36,7 +36,7 @@ struct cloud_data
 {
     /// A (relatively) short name for the cloud. May be referenced from lua.
     const char* terse_name;
-    /// Another name for the cloud. If NULL, defaults to terse name.
+    /// Another name for the cloud. If nullptr, defaults to terse name.
     const char* verbose_name;
     /// The colour of the cloud in console.
     colour_t colour;
@@ -60,7 +60,7 @@ static const cloud_data clouds[] = {
        15, 46,                                  // base, expected random damage
     },
     // CLOUD_MEPHITIC,
-    { "noxious fumes", NULL,                    // terse, verbose name
+    { "noxious fumes", nullptr,                 // terse, verbose name
       GREEN,                                    // colour
       BEAM_MEPHITIC,                            // beam_effect
       0, 19,                                    // base, expected random damage
@@ -72,29 +72,29 @@ static const cloud_data clouds[] = {
       15, 46,                                   // base, expected random damage
     },
     // CLOUD_POISON,
-    { "poison gas", NULL,                       // terse, verbose name
+    { "poison gas", nullptr,                    // terse, verbose name
       LIGHTGREEN,                               // colour
       BEAM_POISON,                              // beam_effect
       0, 37,                                    // base, expected random damage
     },
     // CLOUD_BLACK_SMOKE,
-    { "black smoke",  NULL,                     // terse, verbose name
+    { "black smoke",  nullptr,                  // terse, verbose name
       DARKGREY,                                 // colour
     },
     // CLOUD_GREY_SMOKE,
-    { "grey smoke",  NULL,                      // terse, verbose name
+    { "grey smoke",  nullptr,                   // terse, verbose name
       LIGHTGREY,                                // colour
     },
     // CLOUD_BLUE_SMOKE,
-    { "blue smoke",  NULL,                      // terse, verbose name
+    { "blue smoke",  nullptr,                   // terse, verbose name
       LIGHTBLUE,                                // colour
     },
     // CLOUD_PURPLE_SMOKE,
-    { "purple smoke",  NULL,                    // terse, verbose name
+    { "purple smoke",  nullptr,                 // terse, verbose name
       MAGENTA,                                  // colour
     },
     // CLOUD_TLOC_ENERGY,
-    { "translocational energy",  NULL,          // terse, verbose name
+    { "translocational energy",  nullptr,       // terse, verbose name
       MAGENTA,                                  // colour
     },
     // CLOUD_FOREST_FIRE,
@@ -116,17 +116,17 @@ static const cloud_data clouds[] = {
     },
 #endif
     // CLOUD_INK,
-    { "ink",  NULL,                             // terse, verbose name
+    { "ink",  nullptr,                          // terse, verbose name
       DARKGREY,                                 // colour
       BEAM_INK,                                 // beam_effect
     },
     // CLOUD_PETRIFY,
-    { "calcifying dust",  NULL,                 // terse, verbose name
+    { "calcifying dust",  nullptr,              // terse, verbose name
       WHITE,                                    // colour
       BEAM_PETRIFYING_CLOUD,                    // beam_effect
     },
     // CLOUD_HOLY_FLAMES,
-    { "blessed fire", NULL,                     // terse, verbose name
+    { "blessed fire", nullptr,                  // terse, verbose name
       ETC_HOLY,                                 // colour
       BEAM_HOLY_FLAME,                          // beam_effect
       15, 46,                                   // base, expected random damage
@@ -137,11 +137,11 @@ static const cloud_data clouds[] = {
       BEAM_MIASMA,                              // beam_effect
     },
     // CLOUD_MIST,
-    { "thin mist", NULL,                        // terse, verbose name
+    { "thin mist", nullptr,                     // terse, verbose name
       ETC_MIST,                                 // colour
     },
     // CLOUD_CHAOS,
-    { "seething chaos", NULL,                   // terse, verbose name
+    { "seething chaos", nullptr,                // terse, verbose name
       ETC_RANDOM,                               // colour
       BEAM_CHAOS,                               // beam_effect
     },
@@ -150,29 +150,29 @@ static const cloud_data clouds[] = {
       ETC_MIST,                                 // colour
     },
     // CLOUD_MUTAGENIC,
-    { "mutagenic fog",  NULL,                   // terse, verbose name
+    { "mutagenic fog",  nullptr,                // terse, verbose name
       ETC_MUTAGENIC,                            // colour
     },
     // CLOUD_MAGIC_TRAIL,
-    { "magical condensation", NULL,             // terse, verbose name
+    { "magical condensation", nullptr,          // terse, verbose name
       ETC_MAGIC,                                // colour
     },
     // CLOUD_TORNADO,
-    { "raging winds", NULL,                     // terse, verbose name
+    { "raging winds", nullptr,                  // terse, verbose name
       ETC_TORNADO,                              // colour
     },
     // CLOUD_DUST_TRAIL,
-    { "sparse dust",  NULL,                     // terse, verbose name
+    { "sparse dust",  nullptr,                  // terse, verbose name
       ETC_EARTH,                                // colour
     },
     // CLOUD_GHOSTLY_FLAME,
-    { "ghostly flame", NULL,                    // terse, verbose name
+    { "ghostly flame", nullptr,                 // terse, verbose name
       ETC_ELECTRICITY,                          // colour
       BEAM_NONE,                                // beam_effect
       0, 25,                                    // base, expected random damage
     },
     // CLOUD_ACID,
-    { "acidic fog", NULL,                       // terse, verbose name
+    { "acidic fog", nullptr,                    // terse, verbose name
       YELLOW,                                   // colour
       BEAM_ACID,                                // beam_effect
       15, 46,                                   // base, random expected damage
@@ -184,7 +184,7 @@ static const cloud_data clouds[] = {
       60, 46,                                   // base, random expected damage
     },
     // CLOUD_NEGATIVE_ENERGY,
-    { "negative energy", NULL,                  // terse, verbose name
+    { "negative energy", nullptr,               // terse, verbose name
       ETC_INCARNADINE,                          // colour
       BEAM_NEG,                                 // beam_effect
       15, 46,                                   // base, random expected damage
@@ -253,7 +253,7 @@ static bool _is_opaque_cloud(cloud_type ctype);
 static void _los_cloud_changed(const coord_def& p, cloud_type t)
 {
     if (_is_opaque_cloud(t))
-        invalidate_los_around(p);
+        los_terrain_changed(p);
 }
 
 static void _new_cloud(int cloud, cloud_type type, const coord_def& p,
@@ -522,7 +522,7 @@ void manage_clouds()
                 if (you_see && !you_worship(GOD_QAZLAL))
                     mpr("Lightning arcs down from a storm cloud!");
                 noisy(spell_effect_noise(SPELL_LIGHTNING_BOLT), cloud.pos,
-                      you_see || you_worship(GOD_QAZLAL) ? NULL
+                      you_see || you_worship(GOD_QAZLAL) ? nullptr
                       : "You hear a mighty clap of thunder!");
             }
             if (grd(cloud.pos) == DNGN_LAVA)
@@ -673,8 +673,8 @@ void swap_clouds(coord_def p1, coord_def p2)
     env.cgrid(p2) = c1;
     if (affects_los)
     {
-        invalidate_los_around(p1);
-        invalidate_los_around(p2);
+        los_terrain_changed(p1);
+        los_terrain_changed(p2);
     }
 }
 
@@ -1056,7 +1056,7 @@ static bool _actor_apply_cloud_side_effects(actor *act,
                                             int final_damage)
 {
     const bool player = act->is_player();
-    monster *mons = !player? act->as_monster() : NULL;
+    monster *mons = !player? act->as_monster() : nullptr;
     switch (cloud.type)
     {
     case CLOUD_RAIN:
@@ -1329,7 +1329,7 @@ static int _actor_cloud_damage(const actor *act,
         noisy(spell_effect_noise(SPELL_LIGHTNING_BOLT), act->pos(),
               act->is_player() || you.see_cell(act->pos())
               || you_worship(GOD_QAZLAL)
-                ? NULL
+                ? nullptr
                 : "You hear a clap of thunder!");
 
         return lightning_dam;
@@ -1352,7 +1352,7 @@ int actor_apply_cloud(actor *act)
 
     const cloud_struct &cloud(env.cloud[cl]);
     const bool player = act->is_player();
-    monster *mons = !player? act->as_monster() : NULL;
+    monster *mons = !player? act->as_monster() : nullptr;
     const beam_type cloud_flavour = _cloud2beam(cloud.type);
 
     if (actor_cloud_immune(act, cloud))
@@ -1393,15 +1393,8 @@ int actor_apply_cloud(actor *act)
              cloud.cloud_name().c_str());
 
         actor *oppressor = cloud.agent();
-
-        if (player)
-        {
-            ouch(final_damage, KILLED_BY_CLOUD,
-                 oppressor ? oppressor->mid : MID_NOBODY,
-                 cloud.cloud_name("", true).c_str());
-        }
-        else
-            mons->hurt(oppressor, final_damage, BEAM_MISSILE);
+        act->hurt(oppressor, final_damage, BEAM_MISSILE,
+                  KILLED_BY_CLOUD, "", cloud.cloud_name("", true));
     }
 
     return final_damage;
@@ -1614,7 +1607,7 @@ string cloud_type_name(cloud_type type, bool terse)
         return "buggy goodness";
 
     ASSERT(clouds[type].terse_name);
-    if (terse || clouds[type].verbose_name == NULL)
+    if (terse || clouds[type].verbose_name == nullptr)
         return clouds[type].terse_name;
     return clouds[type].verbose_name;
 }
@@ -1826,7 +1819,7 @@ static void _spread_cloud(coord_def pos, cloud_type type, int radius, int pow,
         if ((exp_map(*di - pos + centre) < INT_MAX) && env.cgrid(*di) == EMPTY_CLOUD
             && (di.radius() < radius || x_chance_in_y(ratio, 100)))
         {
-            place_cloud(type, *di, pow + random2(pow), NULL);
+            place_cloud(type, *di, pow + random2(pow), nullptr);
             --remaining;
 
             // Setting this way since the agent of the cloud may be dead before
index aeb947f..4920535 100644 (file)
@@ -53,7 +53,7 @@ CLua::CLua(bool managed)
       throttle_sleep_end(800), n_throttle_sleeps(0), mixed_call_depth(0),
       lua_call_depth(0), max_mixed_call_depth(8),
       max_lua_call_depth(100), memory_used(0),
-      _state(NULL), sourced_files(), uniqindex(0)
+      _state(nullptr), sourced_files(), uniqindex(0)
 {
 }
 
@@ -473,7 +473,7 @@ int CLua::return_count(lua_State *ls, const char *format)
     const char *cs = strchr(format, ':');
     if (cs && isdigit(*format))
     {
-        char *es = NULL;
+        char *es = nullptr;
         int ci = strtol(format, &es, 10);
         // We're capping return at 10 here, which is arbitrary, but avoids
         // blowing the stack.
@@ -560,7 +560,7 @@ bool CLua::callbooleanfn(bool def, const char *fn, const char *params, ...)
 
 bool CLua::proc_returns(const char *par) const
 {
-    return strchr(par, '>') != NULL;
+    return strchr(par, '>') != nullptr;
 }
 
 // Identical to lua_getglobal for simple names, but will look up
@@ -790,7 +790,7 @@ void CLua::print_stack()
         lua_getinfo(L, "lnuS", &dbg);
 
         char* file = strrchr(dbg.short_src, '/');
-        if (file == NULL)
+        if (file == nullptr)
             file = dbg.short_src;
         else
             file++;
@@ -910,7 +910,7 @@ bool lua_text_pattern::translate() const
 
     string textp;
     string luafn;
-    const lua_pat_op *currop = NULL;
+    const lua_pat_op *currop = nullptr;
     for (string::size_type i = 0; i < pattern.length(); ++i)
     {
         bool match = false;
@@ -996,13 +996,13 @@ static void *_clua_allocator(void *ud, void *ptr, size_t osize, size_t nsize)
     if (nsize > osize && cl->memory_used >= CLUA_MAX_MEMORY_USE * 1024
         && cl->mixed_call_depth)
     {
-        return NULL;
+        return nullptr;
     }
 
     if (!nsize)
     {
         free(ptr);
-        return NULL;
+        return nullptr;
     }
     else
         return realloc(ptr, nsize);
index c3dc85b..91fef47 100644 (file)
@@ -174,7 +174,7 @@ private:
 
 private:
     void init_lua();
-    void set_error(int err, lua_State *ls = NULL);
+    void set_error(int err, lua_State *ls = nullptr);
     void load_cmacro();
     void load_chooks();
     void init_throttle();
@@ -186,12 +186,12 @@ private:
     bool proc_returns(const char *par) const;
 
     bool calltopfn(lua_State *ls, const char *format, va_list args,
-                   int retc = -1, va_list *fnr = NULL);
+                   int retc = -1, va_list *fnr = nullptr);
     maybe_bool callmbooleanfn(const char *fn, const char *params,
                               va_list args);
 
     int push_args(lua_State *ls, const char *format, va_list args,
-                    va_list *cpto = NULL);
+                    va_list *cpto = nullptr);
     int return_count(lua_State *ls, const char *format);
 
     struct CLuaSave
index 82b6c19..6bd8036 100644 (file)
@@ -86,7 +86,7 @@ void clua_register_metatable(lua_State *ls, const char *tn,
     }
 
     if (lr)
-        luaL_openlib(ls, NULL, lr, 0);
+        luaL_openlib(ls, nullptr, lr, 0);
 }
 
 int clua_pushcxxstring(lua_State *ls, const string &s)
index d8748df..e5f6446 100644 (file)
@@ -56,7 +56,7 @@ void luaopen_setmeta(lua_State *ls,
 
 void clua_register_metatable(lua_State *ls, const char *tn,
                              const luaL_reg *lr,
-                             int (*gcfn)(lua_State *ls) = NULL);
+                             int (*gcfn)(lua_State *ls) = nullptr);
 
 int clua_stringtable(lua_State *ls, const vector<string> &s);
 
@@ -70,7 +70,7 @@ static inline T *clua_get_lightuserdata(lua_State *ls, int ndx)
 {
     return (lua_islightuserdata(ls, ndx))?
             static_cast<T *>(lua_touserdata(ls, ndx))
-          : NULL;
+          : nullptr;
 }
 
 template <class T>
index 717383d..f7fdb9c 100644 (file)
 {'~', CMD_MACRO_ADD},
 #ifdef WIZARD
 {'&', CMD_WIZARD},
+{'+', CMD_EXPLORE_MODE},
 #endif
 {'"', CMD_LIST_JEWELLERY},
 {'{', CMD_INSCRIBE_ITEM},
index 20dc5ff..d52a736 100644 (file)
@@ -647,6 +647,12 @@ void init_element_colours()
                             60,  MAGENTA,
                             60,  RED,
                         0));
+    add_element_colour(_create_random_element_colour_calc(
+                            ETC_SHINING, "shining",
+                            // no YELLOW - always make this visually distinct
+                            60,  WHITE,
+                            60,  BROWN,
+                        0));
     // redefined by Lua later
     add_element_colour(new element_colour_calc(
                             ETC_DISCO, "disco", _etc_random
@@ -793,7 +799,7 @@ int str_to_colour(const string &str, int default_colour, bool accept_number)
     {
         // Check if we have a direct colour index.
         const char *s = str.c_str();
-        char *es = NULL;
+        char *es = nullptr;
         const int ci = static_cast<int>(strtol(s, &es, 10));
         if (s != es && es && ci >= 0 && ci < 16)
             ret = ci;
index 37b5b49..6c5e8f0 100644 (file)
@@ -60,6 +60,7 @@ enum element_type
     ETC_DITHMENOS,      // Dithmenos altar colours
     ETC_ELEMENTAL,      // Cycling elemental colours
     ETC_INCARNADINE,    // Draining clouds coloured like raw flesh
+    ETC_SHINING,        // shining gold (Gozag)
     ETC_DISCO = 96,
     ETC_FIRST_LUA = ETC_DISCO, // colour indices have to be <128
 
index eb212d9..4605449 100644 (file)
@@ -20,6 +20,7 @@
 #include "decks.h"
 #include "describe.h"
 #include "describe-god.h"
+#include "describe-spells.h"
 #include "directn.h"
 #include "english.h"
 #include "env.h"
@@ -155,11 +156,7 @@ static string _get_version_changes()
         {
         highlight:
             // Highlight the Highlights, so to speak.
-            string text  = "<w>";
-                   text += help;
-                   text += "</w>";
-                   text += "\n";
-            result += text;
+            result += "<w>" + help + "</w>\n";
             // And start printing from now on.
             start = true;
         }
@@ -701,7 +698,7 @@ static help_file help_files[] =
 #ifdef USE_TILE_LOCAL
     { "tiles_help.txt",    'T', false },
 #endif
-    { NULL, 0, false }
+    { nullptr, 0, false }
 };
 
 static bool _compare_mon_names(MenuEntry *entry_a, MenuEntry* entry_b)
@@ -746,7 +743,7 @@ public:
         : Menu(_flags, "", _text_only), sort_alpha(true),
           showing_monsters(_show_mon)
         {
-            set_highlighter(NULL);
+            set_highlighter(nullptr);
 
             if (_show_mon)
                 toggle_sorting();
@@ -841,7 +838,7 @@ static vector<string> _get_monster_keys(ucs_t showchar)
 
         const monsterentry *me = get_monster_data(i);
 
-        if (me == NULL || me->name == NULL || me->name[0] == '\0')
+        if (me == nullptr || me->name == nullptr || me->name[0] == '\0')
             continue;
 
         if (me->mc != i)
@@ -920,7 +917,7 @@ static bool _skill_filter(string key, string body)
     for (int i = SK_FIRST_SKILL; i < NUM_SKILLS; i++)
     {
         skill_type sk = static_cast<skill_type>(i);
-        // There are a couple of NULL entries in the skill set.
+        // There are a couple of nullptr entries in the skill set.
         if (!skill_name(sk))
             continue;
 
@@ -1168,7 +1165,7 @@ static int _do_description(string key, string type, const string &suffix,
                     desc += "This book is beyond your current level "
                             "of understanding.";
                 }
-                append_spells(desc, mitm[thing_created]);
+                desc += describe_item_spells(mitm[thing_created]);
             }
         }
     }
@@ -1300,8 +1297,8 @@ static void _find_description(bool *again, string *error_inout)
     string    type;
     string    extra;
     string    suffix;
-    db_find_filter filter     = NULL;
-    db_keys_recap  recap      = NULL;
+    db_find_filter filter     = nullptr;
+    db_keys_recap  recap      = nullptr;
     bool           want_regex = true;
     bool           want_sort  = true;
 
@@ -1359,13 +1356,13 @@ static void _find_description(bool *again, string *error_inout)
         break;
     case 'G':
         type       = "god";
-        filter     = NULL;
+        filter     = nullptr;
         want_regex = false;
         doing_gods = true;
         break;
     case 'B':
         type           = "branch";
-        filter         = NULL;
+        filter         = nullptr;
         want_regex     = false;
         want_sort      = false;
         doing_branches = true;
@@ -1435,7 +1432,7 @@ static void _find_description(bool *again, string *error_inout)
     else
         key_list = _get_desc_keys(regex, filter);
 
-    if (recap != NULL)
+    if (recap != nullptr)
         (*recap)(key_list);
 
     if (key_list.empty())
@@ -1517,7 +1514,7 @@ static void _find_description(bool *again, string *error_inout)
         if (ends_with(str, suffix)) // perhaps we should assert this?
             str.erase(str.length() - suffix.length());
 
-        MenuEntry *me = NULL;
+        MenuEntry *me = nullptr;
 
         if (doing_mons)
         {
@@ -1806,7 +1803,7 @@ static int _show_keyhelp_menu(const vector<formatted_string> &lines,
 
     if (with_manual)
     {
-        for (int i = 0; help_files[i].name != NULL; ++i)
+        for (int i = 0; help_files[i].name != nullptr; ++i)
         {
             // Attempt to open this file, skip it if unsuccessful.
             string fname = canonicalise_file_separator(help_files[i].name);
index 785f0e3..405ef21 100644 (file)
@@ -119,11 +119,11 @@ void crash_signal_handler(int sig_num)
         char name[180];
 
         snprintf(name, sizeof(name), "%scrash-recursive-%s-%s.txt", dir.c_str(),
-                you.your_name.c_str(), make_file_time(time(NULL)).c_str());
+                you.your_name.c_str(), make_file_time(time(nullptr)).c_str());
 
         FILE* file = fopen_replace(name);
 
-        if (file == NULL)
+        if (file == nullptr)
             file = stderr;
 
         write_stack_trace(file, 0);
@@ -261,7 +261,7 @@ void dump_crash_info(FILE* file)
 {
 #if defined(UNIX)
     const char *name = strsignal(_crash_signal);
-    if (name == NULL)
+    if (name == nullptr)
         name = "INVALID";
 
     fprintf(file, "Crash caused by signal #%d: %s\n\n", _crash_signal,
@@ -291,7 +291,7 @@ void write_stack_trace(FILE* file, int ignore_count)
     char **symbols = backtrace_symbols(frames, num_frames);
 
 #if !defined(TARGET_OS_MACOSX)
-    if (symbols == NULL)
+    if (symbols == nullptr)
     {
         fprintf(stderr, "Out of memory.\n");
         fprintf(file,   "Out of memory.\n");
@@ -343,7 +343,7 @@ void write_stack_trace(FILE* file, int ignore_count)
             bt += ": ";
             *lastparen = '\0';
             char *realname = abi::__cxa_demangle(firstparen + 1, 0, 0, &status);
-            if (realname != NULL)
+            if (realname != nullptr)
                 bt += realname;
             free(realname);
         }
index fcf0e6e..509b174 100644 (file)
@@ -84,7 +84,7 @@ static const struct luaL_reg crawl_test_lib[] =
     { "begin_test", crawl_begin_test },
     { "test_success", crawl_test_success },
     { "script_args", crawl_script_args },
-    { NULL, NULL }
+    { nullptr, nullptr }
 };
 
 static void _init_test_bindings()
index 3c0c40e..ceb09ea 100644 (file)
@@ -415,6 +415,8 @@ Xom life saving general
 
 "Not here, not now."
 
+"Just one more try."
+
 Xom is feeling generous.
 %%%%
 Xom life saving actor
@@ -441,6 +443,15 @@ Xom yawns loudly!
 "I guess I need a new plaything now."
 
 "Huh? Did I miss anything?"
+
+"It was about time."
+
+"This toy is broken."
+
+"It's only fun and games until someone loses an eye."
+
+w:1
+"It's not fun and games unless someone loses an eye."
 %%%%
 # Xom laughing
 # (Currently only used post-game in response to "You die...")
index 44fc0a4..3baa06c 100644 (file)
@@ -590,6 +590,14 @@ Throw Icicle shard shrike cast
 Lightning Bolt electric golem cast
 
 @The_monster@ hurls @beam@ @at@ @target@.
+%%%%
+insubstantial wisp cast
+
+@The_monster@ thrums sharply.
+%%%%
+Blink insubstantial wisp cast
+
+__NONE
 ########################################################################
 # Monster species and genus messages.
 ########################################################################
index bc1a998..d4bd85b 100644 (file)
@@ -38,6 +38,7 @@ stop += flesh start
 # Enchantments
 #
 ignore += Your skin is crawling a little less now.
+ignore += of your corpse armour falls away.
 
 # Ghouls
 #
index f5f9a30..f745ef2 100644 (file)
@@ -38,7 +38,7 @@ function callback.tgw_lugonu_bribe_lugonu_item (data, triggerable,
          Elyvilon="lantern of shadows / book of Necromancy w:5 /\
               book of Death w:5 / book of Unlife w:5",
          Fedhas="randbook owner:Lugonu spells:necromutation numspells:1 /\
-              randbook owner:Lugonu spells:simulacrum|twisted_resurrection \
+              randbook owner:Lugonu spells:simulacrum|cigotuvi's_embrace \
                        numspells:2 /\
               randbook owner:Lugonu spells:animate_dead|sublimation_of_blood \
                        numspells:2",
index 3259b3e..7d30576 100644 (file)
@@ -494,7 +494,7 @@ TAGS: grunt_profane_temple no_monster_gen
 NSUBST: 4 = 8:4 / *:., 5 = 1:6 / *:5
 KMONS: 4 = blue devil / simulacrum w:20 / freezing wraith
 KMONS: 5 = ice devil
-KMONS: 6 = blizzard demon / ice dragon
+KMONS: 6 = blizzard demon / shard shrike
 KMONS: 8 = ice statue
 KFEAT: A = stone_arch
 MARKER: A = lua:props_marker { portal=1 }
@@ -821,7 +821,7 @@ MONS:    fire dragon w:16 / ice dragon w:16 / swamp dragon w:5              / \
          hydra w:5 / anaconda w:5 / shock serpent w:5                       / \
          snapping turtle w:5 / harpy w:5 / hell beast
 MONS:    storm dragon w:20 / shadow dragon w:20 / iron dragon               / \
-         golden dragon / quicksilver dragon w:15                            / \
+         golden dragon / quicksilver dragon w:15 / caustic shrike           / \
          torpor snail / emperor scorpion w:5 / hellephant w:5               / \
          ghost moth w:3 / death drake w:2 / alligator snapping turtle w:5
 MONS:    great orb of eyes / eye of draining / ugly thing                   / \
index a5fd4f9..1b8eda2 100644 (file)
@@ -155,13 +155,13 @@ KPROP:   l = no_cloud_gen
 KMONS:   1 = ice fiend
 KMONS:   2 = blizzard demon
 KMONS:   34 = ice devil / blue devil
-KMONS:   56 = reaper ; scythe ego:freezing ident:type
+KMONS:   56 = reaper ; scythe ego:freezing ident:type / shard shrike w:5
 KMONS:   7 = alligator snapping turtle skeleton / anaconda simulacrum / \
          golden dragon simulacrum w:5 / iron troll simulacrum w:5 / hydra simulacrum
 KMONS:   0 = skeletal warrior ; hunting sling ego:freezing ident:type . sling bullet
 SUBST:   l = w, W = .
 :  else
-KMONS:   1 = kraken simulacrum
+KMONS:   1 = shard shrike
 KMONS:   2 = seven-headed hydra simulacrum / eight-headed hydra simulacrum
 KMONS:   3 = orb of fire / hellephant w:5
 KMONS:   4 = draconian scorcher
@@ -196,11 +196,12 @@ TAGS:    no_monster_gen no_pool_fixup allow_dup
 DEPTH:   Geh, Coc, Zot, !Geh:$, !Coc:$, !Zot:$, Abyss
 SHUFFLE: 123456, }] / }] / {[ / **
 :  if you.branch() == "Geh" then
-MONS:    brimstone fiend / balrug / hellion, sun demon / smoke demon / \
-         hell knight
+MONS:    brimstone fiend / balrug / hellion
+MONS:    sun demon / smoke demon / hell knight
 SUBST:   w = l, f = F, %* = ., 2 = 1, 3456 = 2
 :  elseif you.branch() == "Coc" then
-MONS:    ice fiend / blizzard demon / reaper ; scythe ego:freezing ident:type
+MONS:    ice fiend / blizzard demon / shard shrike / \
+         reaper ; scythe ego:freezing ident:type
 MONS:    eight-headed hydra simulacrum w:2 / \
          seven-headed hydra simulacrum w:2 / \
          ice dragon simulacrum w:3 / freezing wraith w:3 / ice devil
index bf5ab46..c85af6e 100644 (file)
@@ -967,6 +967,7 @@ SUBST:   F = 3
 ITEM:    disc of storms, fan of gales, staff of air, book of the sky
 : elseif crawl.coinflip() then
 MONS:    frost giant, ice dragon
+KMONS:   F = shard shrike
 MARKER:  F = lua:fog_machine { cloud_type = "freezing vapour", \
               pow_min = 5, pow_max = 15, delay = 10, size = 2, \
               walk_dist = 0, spread_rate= 0 }
@@ -975,6 +976,7 @@ SUBST:   w = l
 SUBST:   W = l..
 : else
 MONS:    fire giant, fire dragon
+KMONS:   F = salamander firebrand
 MARKER:  F = lua:fog_machine { cloud_type = "flame", pow_min = 5, \
               pow_max = 15, delay = 10, size = 2, walk_dist = 0, \
               spread_rate= 0 }
@@ -1666,7 +1668,7 @@ KFEAT: _ = altar_xom / altar_lugonu / altar_zin / altar_the_shining_one / \
 SUBST: 1 = 1.
 # What kind of holy books would draconians have?
 KITEM: D = book of the Dragon
-KITEM: Z = randbook spells:summon_demon|fireball|twisted_resurrection|airstrike title:the_Zealot
+KITEM: Z = randbook spells:summon_demon|fireball|animate_dead|airstrike title:the_Zealot
 KITEM: F = randbook spells:bolt_of_fire|fireball|sticky_flame|conjure_flame title:Fire_and_Brimstone
 KITEM: _ = manual of invocations
 COLOUR: r = red, y = yellow, b = blue, g = green, m = magenta, q = cyan, c = magenta
@@ -1714,7 +1716,7 @@ KMONS:   H = green draconian
 KMONS:   I = green death / nonbase green draconian w:6
 KMONS:   J = white very ugly thing
 KMONS:   K = white draconian
-KMONS:   L = white draconian knight / nonbase white draconian w:6
+KMONS:   L = shard shrike / nonbase white draconian w:6
 KMONS:   M = brown very ugly thing
 KMONS:   N = yellow draconian
 KMONS:   O = acid blob / nonbase yellow draconian w:6
index 7fedd25..92c6652 100644 (file)
@@ -1395,104 +1395,6 @@ TAGS:   no_rotate no_vmirror no_hmirror
 MAP
 ENDMAP
 
-
-
-##############################################################
-# Dummy layouts that just call C++ functions that do all the work.
-#
-
-##############################################################
-# layout_basic
-#
-# TODO: Remove the C++ code that adds layout type "basic".
-#
-NAME:   layout_basic
-DEPTH:  D, Snake, Crypt, Depths, Zot, Pan
-WEIGHT: 35 (D), 15 (Snake), 10 (Crypt), 20 (Depths), 20 (Zot), 15 (Pan)
-ORIENT: encompass
-TAGS:   overwritable layout allow_dup unrand layout_type_rooms
-TAGS:   no_rotate no_vmirror no_hmirror
-{{
-    if not is_validating() then layout_basic() end
-}}
-MAP
-ENDMAP
-
-##############################################################
-# layout_bigger_room
-#
-# Disabled until we can annoy the player less with water monsters.
-#
-NAME:   layout_bigger_room
-DEPTH:  D:9-, Lair:1-7
-WEIGHT: 0
-ORIENT: encompass
-TAGS:   overwritable layout no_primary_vault allow_dup unrand layout_type_open
-{{
-    if not is_validating() then layout_bigger_room() end
-}}
-MAP
-ENDMAP
-
-##############################################################
-# layout_chaotic_city
-#
-# This layout places many non-overlapping boxes, some hollow.
-#
-# TODO: Lair should only have rock walls.  It should have sometimes
-#       thicker walls, especially on deeper depths so it can be used
-#       for the whole branch.
-#
-NAME:   layout_chaotic_city
-DEPTH:  Lair:1-3, Crypt:1-4, Dis
-WEIGHT: 10, 15 (Crypt)
-ORIENT: encompass
-TAGS:   overwritable layout allow_dup unrand layout_type_city
-TAGS:   no_rotate no_vmirror no_hmirror
-{{
-    if not is_validating() then
-      layout_chaotic_city((you.branch() == "Dis") and "metal_wall" or nil)
-    end
-}}
-MAP
-ENDMAP
-
-##############################################################
-# layout_shoals
-#
-NAME:   layout_shoals
-DEPTH:  Shoals
-WEIGHT: 10
-ORIENT: encompass
-TAGS:   overwritable layout allow_dup unrand layout_type_shoals
-{{
-    if not is_validating() then layout_shoals() end
-}}
-MAP
-ENDMAP
-
-##############################################################
-# layout_basic_swamp
-#
-# Previously named "layout_swamp".
-#
-NAME:   layout_basic_swamp
-DEPTH:  Swamp
-WEIGHT: 20
-ORIENT: encompass
-TAGS:   overwritable layout allow_dup unrand layout_type_swamp
-{{
-    if is_validating() then return; end
-
-    dgn.layout_swamp()
-
-    -- Prevent tele closets
-    zonify.grid_fill_water_zones(1,"tree")
-
-}}
-MAP
-ENDMAP
-
 ##############################################################
 # layout_diamond_mine
 #
diff --git a/crawl-ref/source/dat/des/builder/layout_cc.des b/crawl-ref/source/dat/des/builder/layout_cc.des
new file mode 100644 (file)
index 0000000..f998630
--- /dev/null
@@ -0,0 +1,99 @@
+##############################################################
+# layout_cc.des
+#
+# These layouts just call C++ functions that do all the work.
+#
+
+
+
+##############################################################
+# layout_basic
+#
+# TODO: Fill disconnected areas as per [Mantis->9044].
+#
+NAME:   layout_basic
+DEPTH:  D, Snake, Crypt, Depths, Zot, Pan
+WEIGHT: 35 (D), 15 (Snake), 10 (Crypt), 20 (Depths), 20 (Zot), 15 (Pan)
+ORIENT: encompass
+TAGS:   overwritable layout allow_dup unrand layout_type_rooms
+TAGS:   no_rotate no_vmirror no_hmirror
+{{
+    if not is_validating() then layout_basic() end
+}}
+MAP
+ENDMAP
+
+##############################################################
+# layout_bigger_room
+#
+# Disabled until we can annoy the player less with water monsters.
+#
+NAME:   layout_bigger_room
+DEPTH:  D:9-, Lair:1-7
+WEIGHT: 0
+ORIENT: encompass
+TAGS:   overwritable layout no_primary_vault allow_dup unrand layout_type_open
+{{
+    if not is_validating() then layout_bigger_room() end
+}}
+MAP
+ENDMAP
+
+##############################################################
+# layout_chaotic_city
+#
+# This layout places many non-overlapping boxes, some hollow.
+#
+# TODO: Lair should only have rock walls.  It should have sometimes
+#       thicker walls, especially on deeper depths so it can be used
+#       for the whole branch.
+#
+NAME:   layout_chaotic_city
+DEPTH:  Lair:1-3, Crypt:1-4, Dis
+WEIGHT: 10, 15 (Crypt)
+ORIENT: encompass
+TAGS:   overwritable layout allow_dup unrand layout_type_city
+TAGS:   no_rotate no_vmirror no_hmirror
+{{
+    if not is_validating() then
+      layout_chaotic_city((you.branch() == "Dis") and "metal_wall" or nil)
+    end
+}}
+MAP
+ENDMAP
+
+##############################################################
+# layout_shoals
+#
+NAME:   layout_shoals
+DEPTH:  Shoals
+WEIGHT: 10
+ORIENT: encompass
+TAGS:   overwritable layout allow_dup unrand layout_type_shoals
+{{
+    if not is_validating() then layout_shoals() end
+}}
+MAP
+ENDMAP
+
+##############################################################
+# layout_basic_swamp
+#
+# Previously named "layout_swamp".
+#
+NAME:   layout_basic_swamp
+DEPTH:  Swamp
+WEIGHT: 20
+ORIENT: encompass
+TAGS:   overwritable layout allow_dup unrand layout_type_swamp
+{{
+    if is_validating() then return; end
+
+    dgn.layout_swamp()
+
+    -- Prevent tele closets
+    zonify.grid_fill_water_zones(1,"tree")
+
+}}
+MAP
+ENDMAP
index a1984a5..36446ec 100644 (file)
@@ -1611,7 +1611,8 @@ KITEM:   d = scroll of blinking / scroll of teleportation q:2 / \
              wand of teleportation w:5 / any scroll w:1
 KITEM:   e = ring of teleport control w:9 / ring of teleportation w:20 / any ring w:1
 KITEM:   f = book of spatial translocations w:5 / book of the warp w:5 / \
-             manual of translocations w:5 / randbook disc:translocation numspells:5 / \
+             plane papyrus w:5 / manual of translocations w:5 / \
+             randbook disc:translocation numspells:5 / \
              randbook disc:translocation owner:Golubria numspells:7 \
              spells:shroud_of_golubria|passage_of_golubria
 KITEM:   g = ring of flight / potion of flight q:2 / \
index dbec44f..be15871 100644 (file)
@@ -2479,7 +2479,8 @@ MONS:    plant w:14 / fungus w:4 / bush w:2
 MONS:    fire dragon / ice dragon / lindwurm / hell hound
 MONS:    death yak / griffon / catoblepas / dire elephant
 MONS:    hydra / anaconda / red wasp w:2 / redback w:3 / wolf spider w:5 / harpy
-MONS:    spriggan druid, spriggan rider, sphinx / hell beast
+MONS:    spriggan druid, spriggan rider
+MONS:    sphinx / hell beast w:5 / caustic shrike w:5
 ITEM:    fruit q:1
 NSUBST:  ' : 1:2 / 1:3 / 1:4 / 2 = 233444. / *:.
 NSUBST:  " : 2:2 / 2:3 / 2:4 / 4 = 233444. / *:.
index dc66946..86a754e 100644 (file)
@@ -5670,7 +5670,8 @@ COLOUR:  F = lightred
 KMONS:   9 = ice devil / blue devil w:5 / ice dragon / simulacrum w:5 / \
              skeletal warrior ; hunting sling ego:freezing ident:type . \
              mundane sling bullet . mace ego:freezing ident:type
-KMONS:   8 = blizzard demon / lich / reaper ; scythe ego:freezing ident:type
+KMONS:   8 = blizzard demon / lich / shard shrike / \
+             reaper ; scythe ego:freezing ident:type
 KMONS:   7 = ice fiend
 MARKER:  F = lua:fog_machine {cloud_type = "freezing vapour", \
              pow_min = 8, pow_max = 10, delay = 100, \
index 9689128..b2d788d 100644 (file)
@@ -160,6 +160,15 @@ CMD_WAIT verbose
 
 Spend a single turn waiting and resting.
 %%%%
+CMD_SAFE_WAIT
+
+Wait a single turn if safe
+%%%%
+CMD_SAFE_WAIT verbose
+
+Spend a single turn waiting and resting, but only if there are no hostile
+monsters in view.
+%%%%
 CMD_DISPLAY_INVENTORY
 
 Display the contents of your inventory
index 5a352e0..ea38f17 100644 (file)
@@ -66,10 +66,10 @@ Makhleb
 
 Makhleb the Destroyer is a fearsome deity of bloodshed and mortification of the
 flesh. Followers are expected to cleanse others by inflicting suffering and
-death on them in Makhleb's name, and can gain further favour by sacrificing the
-freshly dead. Dedicated followers may also access the raw energies and
-hell-spawned servants of chaos, so that they may better break the impure world
-and its false laws for their evil god's glory.
+death on them in Makhleb's name, particularly if they kill natural creatures
+which require blood to survive. Dedicated followers may also access the raw
+energies and hell-spawned servants of chaos, so that they may better break the
+impure world and its false laws for their evil god's glory.
 %%%%
 Sif Muna
 
index 69040cd..9132538 100644 (file)
@@ -818,6 +818,13 @@ An enchanted vessel brimming over with elemental water. Removing the stopper
 unleashes a torrent of water from the phial and sets free the water spirits
 that dwell within it.
 %%%%
+plane papyrus
+
+The last and greatest work of the Warper Golubria, detailing the most powerful
+Translocations spells in existence. It is notoriously difficult to track down
+and understand, as copies of the book and their contents have a tendency to
+shift between planes at a moment's notice.
+%%%%
 plate armour
 
 A full suit of solid metal plate: cuirass, pauldrons, vambraces, waist plate
index ac5592c..869f5bf 100644 (file)
@@ -1510,7 +1510,7 @@ infernal demonspawn
 insubstantial wisp
 
 A thin wisp of floating gas, flickering, disappearing and reforming in a
-disconcertingly random fashion.
+disconcertingly random fashion while building up an electrical charge.
 %%%%
 iron dragon
 
@@ -2188,6 +2188,11 @@ simulacrum
 
 An ice replica of a monster, animated by the powers of necromancy.
 %%%%
+singularity
+
+A gravitational singularity, which pulls in enemies of its creator and
+violently warps them in the process.
+%%%%
 siren
 
 A beguiling merfolk whose voice can mesmerize listeners.
index 13e2675..41a2e66 100644 (file)
@@ -445,6 +445,14 @@ Ilsuiw
  Till human voices wake us, and we drown.”
     -T.S. Eliot, _The Love Song of J. Alfred Prufrock_. lines 129-131. 1915.
 %%%%
+Irradiate spell
+
+“Reflex in the sky warn you you're gonna die
+ Storm coming, you'd better hide from the atomic tide
+ Flashes in the sky turns houses into sties
+ Turns people into clay, radiation minds decay”
+    -Black Sabbath, “Electric Funeral”. 1970.
+%%%%
 Khufu
 
 “And then I looked farther, beyond the pallid line of the sands, and I saw a
@@ -464,6 +472,22 @@ close-barred sties. And art thou come to release them? Nay, I tell thee, thou
 shalt not thyself return, but shalt remain there with the others.”
     -Homer, Odysseia
 %%%%
+Lee's Rapid Deconstruction spell
+
+“Now the house was full of men and women; and all the lords of the Philistines
+were there; and there were upon the roof about three thousand men and women,
+that beheld while Samson made sport.
+
+And Samson called unto the LORD, and said, O Lord GOD, remember me, I pray
+thee, and strengthen me, I pray thee, only this once, O God, that I may be at
+once avenged of the Philistines for my two eyes.
+
+And Samson said, Let me die with the Philistines. And he bowed himself with all
+his might; and the house fell upon the lords, and upon all the people that were
+therein. So the dead which he slew at his death were more than they which he
+slew in his life.”
+    -KJV Bible, Judges 16:28-30.
+%%%%
 Mara
 
 “This night the Lord of Illusion passed among you, Mara, mighty among dreamers,
@@ -574,6 +598,15 @@ Psyche
     -Apuleius, _Asinus aureus_, “Cupid and Psyche”. circa. 160 AD.
     trans. William Adlington, 1566.
 %%%%
+Shatter spell
+
+“And it happened when the people heard the sound of the trumpet, and the people
+shouted with a great shout, that the wall fell down flat. Then the people went
+up into the city, every man straight before him, and they took the city. And
+they utterly destroyed all that was in the city, both man and woman, young and
+old, ox and sheep and donkey, with the edge of the sword.”
+    -KJV Bible, Joshua 6:20-21.
+%%%%
 Shoals
 
 “I often think about that old metaphor, the one that says we are all islands on
@@ -730,7 +763,7 @@ amulet of stasis
 amulet of the gourmand
 
 “put a knife to thy throat, if thou be a man given to appetite.”
-    -KJV Bible, Proverbs 23:2
+    -KJV Bible, Proverbs 23:2.
 %%%%
 amulet of warding
 
@@ -808,8 +841,12 @@ cult image.”
 %%%%
 battlesphere
 
-Rule 4: Close air support covereth a multitude of sins.
-    -The Seven Habits of Highly Effective Pirates
+“I'm your only friend
+ I'm not your only friend
+ But I'm a little glowing friend
+ But really I'm not actually your friend
+ But I am”
+    -They Might Be Giants, “Birdhouse in Your Soul”. 1989.
 %%%%
 blowgun
 
@@ -1593,6 +1630,12 @@ insubstantial wisp
  There swallow'd up and lost, from succour farr.”
     -John Milton, _Paradise Lost_
 %%%%
+Iskenderun's Battlesphere spell
+
+“Rule 4: Close air support covereth a multitude of sins.”
+    -Howard Tayler, _The Seven Habits of Highly Effective Pirates_,
+     in _Schlock Mercenary_. 2008.
+%%%%
 jackal
 
 Always ready to take advantage of every favourable opportunity, the Jackal is a
@@ -2782,6 +2825,18 @@ staff of wizardry
 
 <staff>
 %%%%
+starcursed mass
+
+“Did you see that star go out?
+ I seen it burn.
+
+ That little star went out,
+ your little eyes went out.
+
+ Our burnt little dreams are hid
+ up where the stars get lit.”
+    -Thee Silver Mt. Zion, “I Built Myself a Metal Bird”. 2010.
+%%%%
 steam dragon armour
 
 <steam dragon hide>
index 9fa4408..816eb4d 100644 (file)
@@ -251,6 +251,14 @@ Chilling Breath spell
 This creatue can breath a blast of frost that bypasses any armour the unfortunate
 victim may have, and might even knock it backwards if in flight.
 %%%%
+Cigotuvi's Embrace spell
+
+This spell violently mutilates all nearby corpses, wrapping them around the
+caster to serve as both armour and shield. As it takes hits, the carrion will
+gradually fall away, faster and faster as more is piled on, though increased
+spellpower will tighten its grasp on the caster's body. It cannot grip ice or
+stone; only that which lives, or once lived.
+%%%%
 Cleansing Flame spell
 
 <Cleansing Flame ability>
@@ -1201,6 +1209,11 @@ magic is unstable, so eventually the replica will sublimate into a freezing
 cloud, if it isn't hacked or melted into a small puddle of water first. Note
 that simulacra are incapable of leaving the level they were created on.
 %%%%
+Singularity spell
+
+This spell compresses space into a gravitational singularity, pulling nearby
+enemies of the caster towards it and warping them violently in the process.
+%%%%
 Siren Song spell
 
 Sings a haunting song, making nearby victims unwilling to move away from the
index a2d1296..8bf3ecd 100644 (file)
@@ -202,8 +202,7 @@ end
 
 mset(with_props("place:Lair:$ w:165 / dire elephant w:12 / " ..
                 "catoblepas w:12 / hellephant w:6 / spriggan druid w:1 / " ..
-                "guardian serpent w:1 / deep troll shaman w:1 / " ..
-                "raiju w:1 / hell beast w:1", { weight = 5 }),
+                "caustic shrike w:4", { weight = 5 }),
      with_props("place:Shoals:$ w:125 band / merfolk aquamancer / " ..
                 "water nymph w:5 / merfolk impaler w:5 / " ..
                 "merfolk javelineer / octopode crusher w:12", { weight = 5 }),
@@ -227,9 +226,9 @@ mset(with_props("place:Lair:$ w:165 / dire elephant w:12 / " ..
                 "frost giant / ettin / titan", { weight = 2 }),
      with_props("fire elemental / fire drake / hell hound / efreet / " ..
                 "fire dragon / fire giant / orb of fire", { weight = 2 }),
-     with_props("ice beast / freezing wraith / ice dragon / " ..
-                "frost giant / ice devil / ice fiend / simulacrum / " ..
-                "white draconian knight / blizzard demon", { weight = 2 }),
+     with_props("ice beast / ice dragon / frost giant / " ..
+                "ice devil / blizzard demon / ice fiend / simulacrum / " ..
+                "white draconian knight / shard shrike", { weight = 2 }),
      with_props("insubstantial wisp / air elemental / titan / raiju / " ..
                 "storm dragon / electric golem / spriggan air mage / " ..
                 "shock serpent", { weight = 2 }),
@@ -265,7 +264,7 @@ mset(spec_fn(function ()
                local d = 290 - 10 * you.depth()
                local e = math.max(0, you.depth() - 20)
                return "place:Orc:$ w:" .. d .. " / orc warlord / " ..
-                 "orc high priest band / orc sorcerer w:5 / stone giant / " ..
+                 "orc high priest / orc sorcerer w:5 / stone giant / " ..
                  "iron troll w:5 / moth of wrath w:" .. e
              end))
 
index 2feda7f..7d69a6f 100644 (file)
@@ -84,12 +84,12 @@ static TextDB AllDBs[] =
             "ability.txt",
             "cards.txt",
             "commands.txt",
-            NULL),
+            nullptr),
 
     TextDB("gamestart", "descript/",
             "species.txt",
             "backgrounds.txt",
-            NULL),
+            nullptr),
 
     TextDB("randart", "database/",
             "randname.txt",
@@ -99,7 +99,7 @@ static TextDB AllDBs[] =
             "randbook.txt", // artefact books
             // This doesn't really belong here, but they *are* god gifts...
             "monname.txt",  // orcish names for Beogh to choose from
-            NULL),
+            nullptr),
 
     TextDB("speak", "database/",
             "monspeak.txt", // monster speech
@@ -108,35 +108,35 @@ static TextDB AllDBs[] =
             "wpnnoise.txt", // noisy weapon speech
             "insult.txt",   // imp/demon taunts
             "godspeak.txt", // god speech
-            NULL),
+            nullptr),
 
     TextDB("shout", "database/",
             "shout.txt",
             "insult.txt",   // imp/demon taunts, again
-            NULL),
+            nullptr),
 
     TextDB("misc", "database/",
             "miscname.txt", // names for miscellaneous things
             "godname.txt",  // god-related names (mostly His Xomminess)
             "montitle.txt", // titles for monsters (i.e. uniques)
-            NULL),
+            nullptr),
 
     TextDB("quotes", "descript/",
             "quotes.txt",   // quotes for items and monsters
-            NULL),
+            nullptr),
 
     TextDB("help", "database/",
             "help.txt",     // database for outsourced help texts
-            NULL),
+            nullptr),
 
     TextDB("FAQ", "database/",
             "FAQ.txt",      // database for Frequently Asked Questions
-            NULL),
+            nullptr),
 
     TextDB("hints", "descript/",
             "hints.txt",    // hints mode
             "tutorial.txt", // tutorial mode
-            NULL),
+            nullptr),
 };
 
 static TextDB& DescriptionDB = AllDBs[0];
@@ -163,7 +163,7 @@ static string _db_cache_path(string db, const char *lang)
 
 TextDB::TextDB(const char* db_name, const char* dir, ...)
     : _db_name(db_name), _directory(dir),
-      _db(NULL), timestamp(""), _parent(0), translation(0)
+      _db(nullptr), timestamp(""), _parent(0), translation(0)
 {
     va_list args;
     va_start(args, dir);
@@ -183,7 +183,7 @@ TextDB::TextDB(const char* db_name, const char* dir, ...)
 
 TextDB::TextDB(TextDB *parent)
     : _db_name(parent->_db_name),
-      _db(NULL), timestamp(""), _parent(parent), translation(0)
+      _db(nullptr), timestamp(""), _parent(parent), translation(0)
 {
     _directory = parent->_directory + Options.lang_name + "/";
     _input_files = parent->_input_files; // FIXME: pointless copy
@@ -232,7 +232,7 @@ void TextDB::shutdown(bool recursive)
     if (_db)
     {
         dbm_close(_db);
-        _db = NULL;
+        _db = nullptr;
     }
     if (recursive && translation)
         translation->shutdown(recursive);
@@ -376,7 +376,7 @@ void databaseSystemShutdown()
 static datum _database_fetch(DBM *database, const string &key)
 {
     datum result;
-    result.dptr = NULL;
+    result.dptr = nullptr;
     result.dsize = 0;
     datum dbKey;
 
@@ -393,20 +393,20 @@ static datum _database_fetch(DBM *database, const string &key)
 static vector<string> _database_find_keys(DBM *database,
                                           const string &regex,
                                           bool ignore_case,
-                                          db_find_filter filter = NULL)
+                                          db_find_filter filter = nullptr)
 {
     text_pattern             tpat(regex, ignore_case);
     vector<string> matches;
 
     datum dbKey = dbm_firstkey(database);
 
-    while (dbKey.dptr != NULL)
+    while (dbKey.dptr != nullptr)
     {
         string key((const char *)dbKey.dptr, dbKey.dsize);
 
         if (tpat.matches(key)
             && key.find("__") == string::npos
-            && (filter == NULL || !(*filter)(key, "")))
+            && (filter == nullptr || !(*filter)(key, "")))
         {
             matches.push_back(key);
         }
@@ -420,14 +420,14 @@ static vector<string> _database_find_keys(DBM *database,
 static vector<string> _database_find_bodies(DBM *database,
                                             const string &regex,
                                             bool ignore_case,
-                                            db_find_filter filter = NULL)
+                                            db_find_filter filter = nullptr)
 {
     text_pattern             tpat(regex, ignore_case);
     vector<string> matches;
 
     datum dbKey = dbm_firstkey(database);
 
-    while (dbKey.dptr != NULL)
+    while (dbKey.dptr != nullptr)
     {
         string key((const char *)dbKey.dptr, dbKey.dsize);
 
@@ -436,7 +436,7 @@ static vector<string> _database_find_bodies(DBM *database,
 
         if (tpat.matches(body)
             && key.find("__") == string::npos
-            && (filter == NULL || !(*filter)(key, body)))
+            && (filter == nullptr || !(*filter)(key, body)))
         {
             matches.push_back(key);
         }
index b6858d9..4005ce6 100644 (file)
@@ -34,9 +34,9 @@ string getQuoteString(const string &key);
 string getLongDescription(const string &key);
 
 vector<string> getLongDescKeysByRegex(const string &regex,
-                                      db_find_filter filter = NULL);
+                                      db_find_filter filter = nullptr);
 vector<string> getLongDescBodiesByRegex(const string &regex,
-                                        db_find_filter filter = NULL);
+                                        db_find_filter filter = nullptr);
 
 string getGameStartDescription(const string &key);
 
index e29fafd..fc60d8a 100644 (file)
@@ -416,9 +416,9 @@ static void _debug_marker_scan()
     {
         map_marker* marker = markers[i];
 
-        if (marker == NULL)
+        if (marker == nullptr)
         {
-            mprf(MSGCH_ERROR, "Marker #%d is NULL", i);
+            mprf(MSGCH_ERROR, "Marker #%d is nullptr", i);
             continue;
         }
 
@@ -455,9 +455,9 @@ static void _debug_marker_scan()
         {
             map_marker *marker = at_pos[i];
 
-            if (marker == NULL)
+            if (marker == nullptr)
             {
-                mprf(MSGCH_ERROR, "Marker #%d at (%d, %d) NULL",
+                mprf(MSGCH_ERROR, "Marker #%d at (%d, %d) nullptr",
                      i, ri->x, ri->y);
                 continue;
             }
@@ -483,7 +483,7 @@ static void _debug_dump_markers()
     {
         map_marker* marker = markers[i];
 
-        if (marker == NULL || marker->get_type() == MAT_LUA_MARKER)
+        if (marker == nullptr || marker->get_type() == MAT_LUA_MARKER)
             continue;
 
         mprf(MSGCH_DIAGNOSTICS, "Marker %d at (%d, %d): %s",
@@ -500,7 +500,7 @@ static void _debug_dump_lua_markers(FILE *file)
     {
         map_marker* marker = markers[i];
 
-        if (marker == NULL || marker->get_type() != MAT_LUA_MARKER)
+        if (marker == nullptr || marker->get_type() != MAT_LUA_MARKER)
             continue;
 
         map_lua_marker* lua_marker = dynamic_cast<map_lua_marker*>(marker);
@@ -612,7 +612,7 @@ void do_crash_dump()
     }
 
     // Want same time for file name and crash milestone.
-    const time_t t = time(NULL);
+    const time_t t = time(nullptr);
 
     string dir = (!Options.morgue_dir.empty() ? Options.morgue_dir :
                   !SysEnv.crawl_dir.empty()   ? SysEnv.crawl_dir
@@ -639,7 +639,7 @@ void do_crash_dump()
 
     // The errno values are only relevant when the function in
     // question has returned a value indicating (possible) failure, so
-    // only freak out if freopen() returned NULL!
+    // only freak out if freopen() returned nullptr!
     if (!file)
     {
         fprintf(stdout, "\nUnable to open file '%s' for writing: %s\n",
@@ -649,7 +649,7 @@ void do_crash_dump()
 
     // Unbuffer the file, since if we recursively crash buffered lines
     // won't make it to the file.
-    setvbuf(file, NULL, _IONBF, 0);
+    setvbuf(file, nullptr, _IONBF, 0);
 
     set_msg_dump_file(file);
 
@@ -751,7 +751,7 @@ void do_crash_dump()
     _debug_dump_lua_markers(file);
     fprintf(file, ">>>>>>>>>>>>>>>>>>>>>>\n");
 
-    set_msg_dump_file(NULL);
+    set_msg_dump_file(nullptr);
 
     mark_milestone("crash", _assert_msg, "", t);
 
@@ -794,7 +794,7 @@ NORETURN static void _BreakStrToDebugger(const char *mesg, bool assert)
 
 #if defined(TARGET_OS_MACOSX)
 // raise(SIGINT);               // this is what DebugStr() does on OS X according to Tech Note 2030
-    int* p = NULL;              // but this gives us a stack crawl...
+    int* p = nullptr;           // but this gives us a stack crawl...
     *p = 0;
 #endif
 
index d326f9c..cf79351 100644 (file)
@@ -160,7 +160,7 @@ void debug_item_scan()
         // (except to make sure that the monster is alive).
         if (mitm[i].pos.origin())
             _dump_item(name, i, mitm[i], "Unlinked temporary item:");
-        else if (mon != NULL && mon->type == MONS_NO_MONSTER)
+        else if (mon != nullptr && mon->type == MONS_NO_MONSTER)
             _dump_item(name, i, mitm[i], "Unlinked item held by dead monster:");
         else if ((mitm[i].pos.x > 0 || mitm[i].pos.y > 0) && !visited[i])
         {
@@ -202,10 +202,10 @@ void debug_item_scan()
         //
         // Theoretically some of these could match random names.
         //
-        if (strstr(name, "questionable") != NULL
-            || strstr(name, "eggplant") != NULL
-            || strstr(name, "buggy") != NULL
-            || strstr(name, "buggi") != NULL)
+        if (strstr(name, "questionable") != nullptr
+            || strstr(name, "eggplant") != nullptr
+            || strstr(name, "buggy") != nullptr
+            || strstr(name, "buggi") != nullptr)
         {
             _dump_item(name, i, mitm[i], "Bad item:");
         }
@@ -461,7 +461,7 @@ void debug_mons_scan()
 
             const monster* holder = item.holding_monster();
 
-            if (holder == NULL)
+            if (holder == nullptr)
             {
                 _announce_level_prob(warned);
                 warned = true;
@@ -738,7 +738,8 @@ const static char *stat_out_ext = ".txt";
 #define STAT_PRECISION 2
 
 // This must match the order of item_fields
-enum item_base_type {
+enum item_base_type
+{
     ITEM_FOOD,
     ITEM_GOLD,
     ITEM_SCROLLS,
@@ -759,7 +760,8 @@ enum item_base_type {
     ITEM_IGNORE = 100,
 };
 
-enum antiquity_level {
+enum antiquity_level
+{
     ANTIQ_ORDINARY,
     ANTIQ_ARTEFACT,
     ANTIQ_ALL,
index 050cb87..1c5a2a0 100644 (file)
@@ -768,7 +768,7 @@ static int _choose_inventory_deck(const char* prompt)
 {
     const int slot = prompt_invent_item(prompt,
                                         MT_INVLIST, OSEL_DRAW_DECK,
-                                        true, true, true, 0, -1, NULL,
+                                        true, true, true, 0, -1, nullptr,
                                         OPER_EVOKE);
 
     if (prompt_failed(slot))
@@ -958,6 +958,7 @@ static void _describe_cards(vector<card_type> cards)
 #endif
 
     ostringstream data;
+    bool first = true;
     for (card_type card : cards)
     {
         string name = card_name(card);
@@ -966,14 +967,16 @@ static void _describe_cards(vector<card_type> cards)
             desc = "No description found.";
 
         name = uppercase_first(name);
+        if (first)
+            first = false;
+        else
+            data << "\n";
         data << "<w>" << name << "</w>\n"
              << get_linebreak_string(desc, get_number_of_cols() - 1)
              << "\n" << which_decks(card) << "\n";
     }
-    formatted_string fs = formatted_string::parse_string(data.str());
-    clrscr();
-    fs.display();
-    getchm();
+    formatted_scroller fs(0, data.str());
+    fs.show();
 }
 
 // Stack a deck: look at the next five cards, put them back in any
@@ -1858,7 +1861,7 @@ static void _damaging_card(card_type card, int power, deck_rarity_type rarity,
     }
 
     if (spell_direction(target, beam, DIR_NONE, TARG_HOSTILE,
-                        LOS_RADIUS, true, true, false, NULL, prompt.c_str())
+                        LOS_RADIUS, true, true, false, nullptr, prompt.c_str())
         && player_tracer(ZAP_DEBUGGING_RAY, power/6, beam))
     {
         if (you.confused())
@@ -2402,7 +2405,7 @@ static void _summon_dancing_weapon(int power, deck_rarity_type rarity)
     if (mon)
     {
         // Override the weapon.
-        ASSERT(mon->weapon() != NULL);
+        ASSERT(mon->weapon() != nullptr);
         item_def& wpn(*mon->weapon());
 
         if (power_level == 0)
@@ -2605,6 +2608,7 @@ static void _mercenary_card(int power, deck_rarity_type rarity)
 
     int merc;
     monster *mon;
+    bool hated = player_mutation_level(MUT_NO_LOVE);
 
     while (1)
     {
@@ -2635,6 +2639,9 @@ static void _mercenary_card(int power, deck_rarity_type rarity)
             return;
         }
 
+        // always hostile, don't try to find a good one
+        if (hated)
+            break;
         if (player_will_anger_monster(mon))
         {
             dprf("God %s doesn't like %s, retrying.",
@@ -2650,6 +2657,12 @@ static void _mercenary_card(int power, deck_rarity_type rarity)
 
     redraw_screen(); // We want to see the monster while it's asking to be paid.
 
+    if (hated)
+    {
+        simple_monster_message(mon, " is unwilling to work for you!");
+        return;
+    }
+
     const int fee = fuzz_value(exper_value(mon), 15, 15);
     if (fee > you.gold)
     {
index e828e60..fc00c13 100644 (file)
@@ -409,6 +409,14 @@ bool player_stair_delay()
            || delay.type == DELAY_DESCENDING_STAIRS;
 }
 
+/**
+ * Is the player currently in the middle of memorizing a spell?
+ *
+ * @param spell     A specific spell, or -1 to check if we're memorizing any
+ *                  spell at all.
+ * @return          Whether the player is currently memorizing the given type
+ *                  of spell.
+ */
 bool already_learning_spell(int spell)
 {
     if (!you_are_delayed())
@@ -1205,7 +1213,7 @@ static string _abyss_monster_creation_message(const monster* mon)
 static inline bool _monster_warning(activity_interrupt_type ai,
                                     const activity_interrupt_data &at,
                                     delay_type atype,
-                                    vector<string>* msgs_buf = NULL)
+                                    vector<string>* msgs_buf = nullptr)
 {
     if (ai == AI_SENSE_MONSTER)
     {
index 73d42fd..4ced6ef 100644 (file)
@@ -27,7 +27,7 @@ struct activity_interrupt_data
     seen_context_type context;
 
     activity_interrupt_data()
-        : apt(AIP_NONE), data(NULL), context(SC_NONE)
+        : apt(AIP_NONE), data(nullptr), context(SC_NONE)
     {
     }
     activity_interrupt_data(const int *i)
@@ -76,7 +76,7 @@ bool delay_is_run(delay_type delay);
 bool is_being_drained(const item_def &item);
 bool is_vampire_feeding();
 bool player_stair_delay();
-bool already_learning_spell(int spell);
+bool already_learning_spell(int spell = -1);
 
 void clear_macro_process_key_delay();
 
@@ -86,11 +86,11 @@ bool is_delay_interruptible(delay_type delay);
 const char *delay_name(int delay);
 delay_type get_delay(const string &);
 
-void run_macro(const char *macroname = NULL);
+void run_macro(const char *macroname = nullptr);
 
 void autotoggle_autopickup(bool off);
 bool interrupt_activity(activity_interrupt_type ai,
                         const activity_interrupt_data &a
                             = activity_interrupt_data(),
-                        vector<string>* msgs_buf = NULL);
+                        vector<string>* msgs_buf = nullptr);
 #endif
index 5ac1272..db4a159 100644 (file)
@@ -576,7 +576,7 @@ static bool _check_description_cycle(god_desc_type gdesc)
     const int bottom_line = min(30, get_number_of_lines());
 
     cgotoxy(1, bottom_line);
-    const char* place = NULL;
+    const char* place = nullptr;
     switch (gdesc)
     {
         case GDESC_OVERVIEW: place = "<w>Overview</w>|Powers|Wrath"; break;
diff --git a/crawl-ref/source/describe-spells.cc b/crawl-ref/source/describe-spells.cc
new file mode 100644 (file)
index 0000000..44308d4
--- /dev/null
@@ -0,0 +1,267 @@
+/**
+ * @file
+ * @brief Functions used to print information about spells, spellbooks, rods,
+ *        etc.
+ **/
+
+#include "AppHdr.h"
+
+#include "describe-spells.h"
+
+#include "cio.h"
+#include "delay.h"
+#include "describe.h"
+#include "externs.h"
+#include "invent.h"
+#include "libutil.h"
+#include "macro.h"
+#include "prompt.h"
+#include "spl-book.h"
+#include "spl-util.h"
+#include "state.h"
+#include "unicode.h"
+
+
+
+/**
+ * Returns a spellset containing the spells for the given item.
+ *
+ * @param item      The item in question.
+ * @return          A single-element vector, containing the list of all
+ *                  non-null spells in the given book/rod, blank-labeled.
+ */
+spellset item_spellset(const item_def &item)
+{
+    if (!item.has_spells())
+        return {};
+
+    vector<spell_type> spells;
+    for (int i = 0; i < SPELLBOOK_SIZE; i++)
+    {
+        spell_type spell = which_spell_in_book(item, i);
+        if (spell != SPELL_NO_SPELL)
+            spells.emplace_back(spell);
+    }
+    return { { "", spells } };
+}
+
+
+/**
+ * Build a flat vector containing all unique spells in a given spellset.
+ *
+ * @param spellset      The spells in question.
+ * @return              An ordered set of unique spells in the given set.
+ *                      Guaranteed to be in the same order as in the spellset.
+ */
+static vector<spell_type> _spellset_contents(const spellset &spells)
+{
+    vector<spell_type> list;
+    for (auto &book : spells)
+        for (auto spell : book.spells)
+            list.emplace_back(spell);
+    return list;
+}
+
+/**
+ * What spell should a given colour be listed with?
+ *
+ * @param spell         The spell in question.
+ * @param source_item   The physical item holding the spells. May be null.
+ */
+static int _spell_colour(spell_type spell, const item_def* const source_item)
+{
+    if (!source_item || source_item->base_type != OBJ_BOOKS)
+        return spell_highlight_by_utility(spell);
+
+    if (you.has_spell(spell))
+        return COL_MEMORIZED;
+
+    // this is kind of ugly.
+    if (!you_can_memorise(spell)
+        || you.experience_level < spell_difficulty(spell)
+        || player_spell_levels() < spell_levels_required(spell)
+        || !player_can_memorise_from_spellbook(*source_item))
+    {
+        return COL_USELESS;
+    }
+
+    if (!you.has_spell(spell))
+        return COL_UNMEMORIZED;
+
+    return spell_highlight_by_utility(spell);
+}
+
+/**
+ * List the name(s) of the school(s) the given spell is in.
+ *
+ * XXX: This is almost certainly duplicating something somewhere. Also, it's
+ * pretty ugly.
+ *
+ * @param spell     The spell in question.
+ * @return          A '/'-separated list of spellschool names.
+ */
+static string _spell_schools(spell_type spell)
+{
+    string schools;
+
+    for (int i = 0; i <= SPTYP_LAST_EXPONENT; i++)
+    {
+        const int school_flag = 1 << i;
+        if (!spell_typematch(spell, school_flag))
+            continue;
+
+        if (!schools.empty())
+            schools += "/";
+        schools += spelltype_long_name(school_flag);
+    }
+
+    return schools;
+}
+
+/**
+ * Describe a given set of spells.
+ *
+ * @param book              A labeled set of spells, corresponding to a book,
+ *                          rod, or monster spellbook.
+ * @param spell_letters     The letters to use for each spell.
+ * @param source_item       The physical item holding the spells. May be null.
+ * @param description[out]  An output string to append to.
+ */
+static void _describe_book(const spellbook_contents &book,
+                           map<spell_type, char> &spell_letters,
+                           const item_def* const source_item,
+                           formatted_string &description)
+{
+    description.textcolour(LIGHTGREY);
+
+    description.cprintf("%s", book.label.c_str());
+
+    description.cprintf("\n\n Spells");
+    // only display type & level for book/rod spells
+    if (source_item)
+        description.cprintf("                             Type                      Level");
+    description.cprintf("\n");
+
+    for (auto spell : book.spells)
+    {
+        description.cprintf(" ");
+
+        description.textcolour(_spell_colour(spell, source_item));
+
+        // don't crash if we have more spells than letters.
+        const char *spell_letter_index = map_find(spell_letters, spell);
+        const char spell_letter = spell_letter_index ?
+                                  index_to_letter(*spell_letter_index) :
+                                  ' ';
+        description.cprintf("%c - %s",
+                            spell_letter,
+                            chop_string(spell_title(spell), 29).c_str());
+
+        // only display type & level for book/rod spells
+        if (!source_item)
+            continue;
+
+        string schools =
+            source_item->base_type == OBJ_RODS ? "Evocations"
+                                               : _spell_schools(spell);
+        description.cprintf("%s%d\n",
+                            chop_string(schools, 30).c_str(),
+                            spell_difficulty(spell));
+    }
+}
+
+
+/**
+ * List a given set of spells.
+ *
+ * @param spells            The set of spells to be listed.
+ * @param source_item       The physical item holding the spells. May be null.
+ * @param description[out]  An output string to append to.
+ */
+void describe_spellset(const spellset &spells,
+                       const item_def* const source_item,
+                       formatted_string &description)
+{
+    // make a map of characters to spells...
+    const vector<spell_type> flat_spells = _spellset_contents(spells);
+    // .. and spells to characters.
+    map<spell_type, char> spell_letters;
+    // TODO: support more than 26 spells
+    for (size_t c = 0; c < flat_spells.size() && c < 26; c++)
+        spell_letters[flat_spells[c]] = (char) c;
+
+    for (auto book : spells)
+        _describe_book(book, spell_letters, source_item, description);
+}
+
+/**
+ * Return a description of the spells in the given item.
+ *
+ * @param item      The book or rod in question.
+ * @return          A column-and-row listing of the spells in the given item,
+ *                  including names, schools & levels.
+ */
+string describe_item_spells(const item_def &item)
+{
+    formatted_string description;
+    describe_spellset(item_spellset(item), &item, description);
+    return description.tostring();
+}
+
+/**
+ * List a given set of spells & allow the player to select them for further
+ * information/interaction.
+ *
+ * @param spells            The set of spells to be listed.
+ * @param source_item       The physical item holding the spells. May be null.
+ * @param initial_desc      A description to prefix the spellset with.
+ */
+void list_spellset(const spellset &spells, const item_def *source_item,
+                   formatted_string &initial_desc)
+{
+    // make a map of characters to spells.
+    const vector<spell_type> flat_spells = _spellset_contents(spells);
+
+    const bool can_memorize =
+        source_item && source_item->base_type == OBJ_BOOKS
+        && in_inventory(*source_item)
+        && player_can_memorise_from_spellbook(*source_item);
+
+    formatted_string &description = initial_desc;
+    describe_spellset(spells, source_item, description);
+
+    description.textcolour(LIGHTGREY);
+    description.cprintf("\n");
+
+    description.cprintf("Select a spell to read its description");
+    if (can_memorize)
+        description.cprintf(", to memorize it or to forget it");
+    description.cprintf(".\n");
+
+    // don't examine spellbooks that have been destroyed (by tearing out a
+    // page for amnesia), and break out of the loop if you start to memorize
+    // something.
+    while ((!source_item || source_item->is_valid())
+           && !already_learning_spell())
+    {
+        if (!crawl_state.is_replaying_keys())
+        {
+            cursor_control coff(false);
+            clrscr();
+
+            description.display();
+        }
+
+        const char input_char = toalower(getchm(KMC_MENU));
+        if (input_char < 'a' || input_char > 'z')
+            return; // TOOD: support more than 26 spells
+
+        const int spell_index = letter_to_index(input_char);
+        ASSERT(spell_index >= 0);
+        if ((size_t) spell_index >= flat_spells.size())
+            return;
+
+        const spell_type chosen_spell = flat_spells[spell_index];
+        describe_spell(chosen_spell, source_item);
+    }
+}
diff --git a/crawl-ref/source/describe-spells.h b/crawl-ref/source/describe-spells.h
new file mode 100644 (file)
index 0000000..76667e8
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * @file
+ * @brief Functions used to print information about spells, spellbooks, rods,
+ *        etc.
+ **/
+
+#ifndef DESCRIBE_SPELLS_H
+#define DESCRIBE_SPELLS_H
+
+#include <map>
+
+#include "enum.h"
+#include "format.h"
+
+/// What's in a given spellbook?
+struct spellbook_contents
+{
+    /// A label for the book.
+    string label;
+    /// The spells contained in the book (or 'book').
+    vector<spell_type> spells;
+};
+
+typedef vector<spellbook_contents> spellset;
+
+spellset item_spellset(const item_def &item);
+void describe_spellset(const spellset &spells,
+                       const item_def* const source_item,
+                       formatted_string &description);
+string describe_item_spells(const item_def &item);
+void list_spellset(const spellset &spells, const item_def *source_item,
+                   formatted_string &initial_desc);
+
+#endif
index 57906ea..0e5c25e 100644 (file)
@@ -22,6 +22,7 @@
 #include "database.h"
 #include "decks.h"
 #include "delay.h"
+#include "describe-spells.h"
 #include "directn.h"
 #include "english.h"
 #include "env.h"
@@ -290,7 +291,7 @@ static vector<string> _randart_propnames(const item_def& item,
     if (is_unrandom_artefact(item))
     {
         const unrandart_entry *entry = get_unrand_entry(item.special);
-        if (entry && entry->inscrip != NULL)
+        if (entry && entry->inscrip != nullptr)
             propnames.push_back(entry->inscrip);
      }
 
@@ -1579,37 +1580,6 @@ static string _describe_deck(const item_def &item)
     return description;
 }
 
-// Adds a list of all spells contained in a book or rod to its
-// description string.
-void append_spells(string &desc, const item_def &item)
-{
-    if (!item.has_spells())
-        return;
-
-    desc += "\n\nSpells                             Type                      Level\n";
-
-    for (int j = 0; j < SPELLBOOK_SIZE; ++j)
-    {
-        spell_type stype = which_spell_in_book(item, j);
-        if (stype == SPELL_NO_SPELL)
-            continue;
-
-        string name = (is_memorised(stype) ? "*" : "");
-                    name += spell_title(stype);
-        desc += chop_string(name, 35);
-
-        string schools;
-        if (item.base_type == OBJ_RODS)
-            schools = "Evocations";
-        else
-            schools = spell_schools_string(stype);
-
-        desc += chop_string(schools, 65 - 36);
-
-        desc += make_stringf("%d\n", spell_difficulty(stype));
-    }
-}
-
 // ========================================================================
 //      Public Functions
 // ========================================================================
@@ -1773,7 +1743,7 @@ string get_item_description(const item_def &item, bool verbose,
         if (!verbose
             && (Options.dump_book_spells || is_random_artefact(item)))
         {
-            append_spells(desc, item);
+            desc += describe_item_spells(item);
             if (desc.empty())
                 need_extra_line = false;
             else
@@ -1917,7 +1887,7 @@ string get_item_description(const item_def &item, bool verbose,
         }
         else if (Options.dump_book_spells)
         {
-            append_spells(desc, item);
+            desc += describe_item_spells(item);
             if (desc.empty())
                 need_extra_line = false;
             else
@@ -2203,14 +2173,6 @@ static bool _can_show_spells(const item_def &item)
                || player_can_memorise_from_spellbook(item));
 }
 
-static void _show_spells(const item_def &item)
-{
-    formatted_string fs;
-    item_def dup = item;
-    spellbook_contents(dup, &fs);
-    fs.display(2, -2);
-}
-
 static void _show_item_description(const item_def &item)
 {
     const unsigned int lineWidth = get_number_of_cols() - 1;
@@ -2233,26 +2195,11 @@ static void _show_item_description(const item_def &item)
         hints_describe_item(item);
 
     if (_can_show_spells(item))
-      _show_spells(item);
-}
-
-static bool _describe_spells(const item_def &item)
-{
-    const int c = getchm();
-    if (c < 'a' || c > 'h')     //jmf: was 'g', but 8=h
     {
-        clear_messages();
-        return false;
+        formatted_string fdesc;
+        fdesc.cprintf("%s", desc.c_str());
+        list_spellset(item_spellset(item), &item, fdesc);
     }
-
-    const int spell_index = letter_to_index(c);
-
-    const spell_type nthing = which_spell_in_book(item, spell_index);
-    if (nthing == SPELL_NO_SPELL)
-        return false;
-
-    describe_spell(nthing, &item);
-    return item.is_valid();
 }
 
 static bool _can_memorise(item_def &item)
@@ -2270,35 +2217,6 @@ static void _update_inscription(item_def &item)
     }
 }
 
-static bool _describe_spellbook(item_def &item)
-{
-    while (true)
-    {
-        // Memorised spell while reading a spellbook.
-        if (already_learning_spell(-1))
-            return false;
-
-        _show_item_description(item);
-        _update_inscription(item);
-
-        cgotoxy(1, wherey());
-        textcolour(LIGHTGREY);
-
-        if (_can_memorise(item) && !crawl_state.player_is_dead())
-        {
-            cprintf("Select a spell to read its description, to "
-                    "memorise it or to forget it.");
-        }
-        else
-            cprintf("Select a spell to read its description.");
-
-        if (_describe_spells(item))
-            continue;
-
-        return true;
-    }
-}
-
 // it takes a key and a list of commands and it returns
 // the command from the list which corresponds to the key
 static command_type _get_action(int key, vector<command_type> actions)
@@ -2340,7 +2258,7 @@ static bool _actions_prompt(item_def &item, bool allow_inscribe, bool do_prompt)
 {
 #ifdef USE_TILE_LOCAL
     PrecisionMenu menu;
-    TextItem* tmp = NULL;
+    TextItem* tmp = nullptr;
     MenuFreeform* freeform = new MenuFreeform();
     menu.set_select_type(PrecisionMenu::PRECISION_SINGLESELECT);
     freeform->init(coord_def(1, 1),
@@ -2579,10 +2497,20 @@ bool describe_item(item_def &item, bool allow_inscribe, bool shopping)
     tiles_crt_control show_as_menu(CRT_MENU, "describe_item");
 #endif
 
-    if (_can_show_spells(item))
-        return _describe_spellbook(item);
-
+    // we might destroy the item, so save this first.
+    const bool item_had_spells = _can_show_spells(item);
     _show_item_description(item);
+
+    // spellbooks & rods have their own UIs, so we don't currently support the
+    // inscribe/drop/etc prompt UI for them.
+    // ...it would be nice if we did, though.
+    if (item_had_spells)
+    {
+        // only continue the inventory loop if we didn't start memorizing a
+        // spell & didn't destroy the item for amnesia.
+        return !already_learning_spell() && item.is_valid();
+    }
+
     _update_inscription(item);
 
     if (allow_inscribe && crawl_state.game_is_tutorial())
@@ -2635,13 +2563,16 @@ void inscribe_item(item_def &item, bool msgwin)
     char buf[79];
     int ret;
     if (msgwin)
-        ret = msgwin_get_line(prompt, buf, sizeof buf, NULL, item.inscription);
+    {
+        ret = msgwin_get_line(prompt, buf, sizeof buf, nullptr,
+                              item.inscription);
+    }
     else
     {
         _safe_newline();
         prompt = "<cyan>" + prompt + "</cyan>";
         formatted_string::parse_string(prompt).display();
-        ret = cancellable_get_line(buf, sizeof buf, NULL, NULL,
+        ret = cancellable_get_line(buf, sizeof buf, nullptr, nullptr,
                                   item.inscription);
     }
 
@@ -2793,7 +2724,7 @@ string get_skill_description(skill_type skill, bool need_title)
 // forget it and BOOK_NEITHER if you can do neither
 static int _get_spell_description(const spell_type spell,
                                    string &description,
-                                   const item_def* item = NULL)
+                                   const item_def* item = nullptr)
 {
     description.reserve(500);
 
@@ -3319,6 +3250,10 @@ static string _monster_spells_description(const monster_info& mi)
     if (mi.type == MONS_PANDEMONIUM_LORD)
         return "It may possess any of a vast number of diabolical powers.\n";
 
+    // Ditto for (a)liches.
+    if (mi.type == MONS_LICH || mi.type == MONS_ANCIENT_LICH)
+        return "It has mastered any of a vast number of powerful spells.\n";
+
     // Show monster spells and spell-like abilities.
     if (!mi.has_spells())
         return "";
@@ -3718,7 +3653,7 @@ static string _monster_stat_description(const monster_info& mi)
         "tiny",
         "very small",
         "small",
-        NULL,     // don't display anything for 'medium'
+        nullptr,     // don't display anything for 'medium'
         "large",
         "very large",
         "giant",
@@ -3893,8 +3828,8 @@ void get_monster_db_desc(const monster_info& mi, describe_info &inf,
         symbol_suffix += symbol;
         symbol_suffix += "_suffix";
 
-        string suffix = getLongDescription(symbol_suffix);
-              suffix += getLongDescription(symbol_suffix + "_examine");
+        string suffix = getLongDescription(symbol_suffix)
+                      + getLongDescription(symbol_suffix + "_examine");
 
         if (!suffix.empty())
             inf.body << "\n" << suffix;
index 28b001e..6cc3ac9 100644 (file)
@@ -46,8 +46,6 @@ struct describe_info
     string quote;
 };
 
-void append_spells(string &desc, const item_def &item);
-
 bool is_dumpable_artefact(const item_def &item);
 
 string get_item_description(const item_def &item, bool verbose,
@@ -72,7 +70,7 @@ void get_monster_db_desc(const monster_info &mi, describe_info &inf,
                          bool &has_stat_desc, bool force_seen = false);
 
 void get_spell_desc(const spell_type spell, describe_info &inf);
-void describe_spell(spell_type spelled, const item_def* item = NULL);
+void describe_spell(spell_type spelled, const item_def* item = nullptr);
 
 string short_ghost_description(const monster *mon, bool abbrev = false);
 string get_ghost_description(const monster_info &mi, bool concise = false);
index 846860e..75f0da0 100644 (file)
@@ -516,7 +516,7 @@ void dgn_build_labyrinth_level()
 
     if (!vault || !dgn_safe_place_map(vault, true, false))
     {
-        vault = NULL;
+        vault = nullptr;
         _labyrinth_place_exit(end);
     }
     else
index 21edcc4..074c20b 100644 (file)
@@ -49,9 +49,6 @@ void dgn_build_basic_level()
 {
     int level_number = env.absdepth0;
 
-    env.level_build_method += " basic";
-    env.level_layout_types.insert("basic");
-
     int corrlength = 2 + random2(14);
     int no_corr = (one_chance_in(100) ? 500 + random2(500)
                                       : 30 + random2(200));
@@ -128,9 +125,6 @@ void dgn_build_basic_level()
 
 void dgn_build_bigger_room_level()
 {
-    env.level_build_method += " bigger_room";
-    env.level_layout_types.insert("open");
-
     for (rectangle_iterator ri(10); ri; ++ri)
         if (grd(*ri) == DNGN_ROCK_WALL
             && !map_masked(*ri, MMT_VAULT))
@@ -159,9 +153,10 @@ void dgn_build_bigger_room_level()
 // A more chaotic version of city level.
 void dgn_build_chaotic_city_level(dungeon_feature_type force_wall)
 {
-    env.level_build_method += make_stringf(" chaotic_city [%s]",
+        // TODO: Attach this information to the vault name string
+        //       instead of the build method string.
+    env.level_build_method += make_stringf(" [%s]",
         force_wall == NUM_FEATURES ? "any" : dungeon_feature_name(force_wall));
-    env.level_layout_types.insert("city");
 
     int number_boxes = 5000;
     dungeon_feature_type drawing = DNGN_ROCK_WALL;
@@ -432,7 +427,9 @@ static void _builder_extras(int level_number)
 static bool _octa_room(dgn_region& region, int oblique_max,
                        dungeon_feature_type type_floor)
 {
-    env.level_build_method += make_stringf(" octa_room [%d %s]", oblique_max,
+        // TODO: Attach this information to the vault name string
+        //       instead of the build method string.
+    env.level_build_method += make_stringf(" octa_room [oblique %d, %s]", oblique_max,
                                            dungeon_feature_name(type_floor));
 
     int x,y;
@@ -1043,6 +1040,8 @@ static void _many_pools(dungeon_feature_type pool_type)
     const int num_pools = 20 + random2avg(9, 2);
     int pools = 0;
 
+        // TODO: Attach this information to the vault name string
+        //       instead of the build method string.
     env.level_build_method += make_stringf(" many_pools [%s %d]",
         dungeon_feature_name(pool_type), num_pools);
 
@@ -1092,6 +1091,8 @@ static void _build_river(dungeon_feature_type river_type) //mv
     if (player_in_branch(BRANCH_CRYPT) || player_in_branch(BRANCH_TOMB))
         return;
 
+        // TODO: Attach this information to the vault name string
+        //       instead of the build method string.
     env.level_build_method += make_stringf(" river [%s]",
                                            dungeon_feature_name(river_type));
 
@@ -1140,6 +1141,8 @@ static void _build_lake(dungeon_feature_type lake_type) //mv
     if (player_in_branch(BRANCH_CRYPT) || player_in_branch(BRANCH_TOMB))
         return;
 
+        // TODO: Attach this information to the vault name string
+        //       instead of the build method string.
     env.level_build_method += make_stringf(" lake [%s]",
                                            dungeon_feature_name(lake_type));
 
index c4fbb89..5036714 100644 (file)
@@ -71,7 +71,7 @@ enum tide_direction
 };
 
 static tide_direction _shoals_tide_direction;
-static monster* tide_caller = NULL;
+static monster* tide_caller = nullptr;
 static coord_def tide_caller_pos;
 static int tide_called_turns = 0;
 static int tide_called_peak = 0;
@@ -510,7 +510,7 @@ static void _shoals_plant_cluster(coord_def c, int nplants, int radius,
 
 static void _shoals_plant_supercluster(coord_def c,
                                        dungeon_feature_type favoured_feat,
-                                       grid_bool *verboten = NULL)
+                                       grid_bool *verboten = nullptr)
 {
     _shoals_plant_cluster(c, random_range(10, 17, 2),
                           random_range(3, 9), favoured_feat,
@@ -667,8 +667,9 @@ void dgn_shoals_generate_flora()
 
 void dgn_build_shoals_level()
 {
-    env.level_build_method += make_stringf(" shoals+ [%d]", you.depth);
-    env.level_layout_types.insert("shoals");
+        // TODO: Attach this information to the vault name string
+        //       instead of the build method string.
+    env.level_build_method += make_stringf(" [depth %d]", you.depth);
 
     const int shoals_depth = you.depth - 1;
     dgn_replace_area(0, 0, GXM-1, GYM-1, DNGN_ROCK_WALL, DNGN_OPEN_SEA);
@@ -917,7 +918,7 @@ static void _shoals_tide_sweep_items_clear(coord_def c)
         if (item_is_stationary(item) && !one_chance_in(5))
             continue;
 
-        const coord_def target(_shoals_escape_place_from(c, NULL, &item));
+        const coord_def target(_shoals_escape_place_from(c, nullptr, &item));
         if (!target.origin())
         {
             if (item_is_stationary_net(item))
@@ -960,7 +961,7 @@ static bool _shoals_tide_sweep_actors_clear(coord_def c)
         if (monster_habitable_grid(mvictim, DNGN_DEEP_WATER))
             return true;
     }
-    coord_def evacuation_point(_shoals_escape_place_from(c, victim, NULL));
+    coord_def evacuation_point(_shoals_escape_place_from(c, victim, nullptr));
     // The tide no longer drowns monster/player if it cannot push them
     // out of the way.
     if (evacuation_point.origin())
@@ -1186,7 +1187,7 @@ static monster* _shoals_find_tide_caller()
     for (monster_iterator mi; mi; ++mi)
         if (mi->has_ench(ENCH_TIDE))
             return *mi;
-    return NULL;
+    return nullptr;
 }
 
 void shoals_apply_tides(int turns_elapsed, bool force, bool incremental_tide)
@@ -1304,7 +1305,7 @@ void wizard_mod_tide()
              TIDE_MULTIPLIER);
         mpr("");
         const int res =
-            cancellable_get_line(buf, sizeof buf, NULL, _tidemod_keyfilter);
+            cancellable_get_line(buf, sizeof buf, nullptr, _tidemod_keyfilter);
         clear_messages(true);
         if (key_is_escape(res))
             break;
index a003aca..2069cf3 100644 (file)
@@ -60,13 +60,10 @@ static void _swamp_apply_features(int margin)
 
 void dgn_build_swamp_level()
 {
-    env.level_build_method += " swamp";
-    env.level_layout_types.insert("swamp");
-
     const int swamp_depth = you.depth - 1;
     dgn_initialise_heightmap(-19);
     _swamp_slushy_patches(swamp_depth * 3);
     dgn_smooth_heights();
     _swamp_apply_features(2);
-    env.heightmap.reset(NULL);
+    env.heightmap.reset(nullptr);
 }
index 04cb7ae..07711cc 100644 (file)
@@ -20,12 +20,12 @@ void dgn_event_dispatcher::clear()
     listeners.clear();
     for (int y = 0; y < GYM; ++y)
         for (int x = 0; x < GXM; ++x)
-            grid_triggers[x][y].reset(NULL);
+            grid_triggers[x][y].reset(nullptr);
 }
 
 void dgn_event_dispatcher::clear_listeners_at(const coord_def &pos)
 {
-    grid_triggers[pos.x][pos.y].reset(NULL);
+    grid_triggers[pos.x][pos.y].reset(nullptr);
 }
 
 void dgn_event_dispatcher::move_listeners(
index 447931f..2baa117 100644 (file)
@@ -133,7 +133,7 @@ static void _debug_describe_feature_at(const coord_def &where);
 #ifdef WIZARD
 static void _wizard_make_friendly(monster* m)
 {
-    if (m == NULL)
+    if (m == nullptr)
         return;
 
     mon_attitude_type att = m->attitude;
@@ -317,20 +317,20 @@ monster* direction_chooser::targeted_monster() const
     if (m && you.can_see(m))
         return m;
     else
-        return NULL;
+        return nullptr;
 }
 
 // Return your target, if it still exists and is visible to you.
 static monster* _get_current_target()
 {
     if (invalid_monster_index(you.prev_targ))
-        return NULL;
+        return nullptr;
 
     monster* mon = &menv[you.prev_targ];
     if (mon->alive() && you.can_see(mon))
         return mon;
     else
-        return NULL;
+        return nullptr;
 }
 
 string direction_chooser::build_targeting_hint_string() const
@@ -665,7 +665,7 @@ void full_describe_view()
 #endif
             vector<formatted_string> fss;
             formatted_string::parse_string_to_multiple(str, fss);
-            MenuEntry *me = NULL;
+            MenuEntry *me = nullptr;
             for (unsigned int j = 0; j < fss.size(); ++j)
             {
                 if (j == 0)
@@ -947,7 +947,7 @@ range_view_annotator::~range_view_annotator()
 {
     if (crawl_state.darken_range)
     {
-        crawl_state.darken_range = NULL;
+        crawl_state.darken_range = nullptr;
         viewwindow(false);
     }
 }
@@ -966,7 +966,7 @@ monster_view_annotator::~monster_view_annotator()
     if ((Options.use_animations & UA_MONSTER_IN_SIGHT)
         && crawl_state.flash_monsters)
     {
-        crawl_state.flash_monsters = NULL;
+        crawl_state.flash_monsters = nullptr;
         viewwindow(false);
     }
 }
@@ -1024,11 +1024,11 @@ bool direction_chooser::move_is_ok() const
 // Assuming the target is in view, is line-of-fire
 // blocked, and by what?
 static bool _blocked_ray(const coord_def &where,
-                         dungeon_feature_type* feat = NULL)
+                         dungeon_feature_type* feat = nullptr)
 {
     if (exists_ray(you.pos(), where, opc_solid_see))
         return false;
-    if (feat == NULL)
+    if (feat == nullptr)
         return true;
     *feat = ray_blocker(you.pos(), where);
     return true;
@@ -1055,7 +1055,7 @@ bool direction_chooser::find_default_monster_target(coord_def& result) const
 
     // First try to pick our previous target.
     const monster* mons_target = _get_current_target();
-    if (mons_target != NULL
+    if (mons_target != nullptr
         && (mode != TARG_EVOLVABLE_PLANTS
             && mons_attitude(mons_target) == ATT_HOSTILE
             || mode == TARG_ENEMY && !mons_target->friendly()
@@ -2101,7 +2101,7 @@ bool direction_chooser::choose_direction()
                                                  : MOUSE_MODE_TARGET);
     targetter_smite legacy_range(&you, range, 0, 0, true);
     range_view_annotator rva(hitfunc ? hitfunc :
-                             (range >= 0) ? &legacy_range : NULL);
+                             (range >= 0) ? &legacy_range : nullptr);
 
     // init
     moves.delta.reset();
@@ -2339,7 +2339,7 @@ static bool _find_mlist(const coord_def& where, int idx, bool need_path,
         return false;
 
     const monster_info* mon = env.map_knowledge(where).monsterinfo();
-    if (mon == NULL)
+    if (mon == nullptr)
         return false;
 
     int real_idx = 0;
index b14a2cc..099345b 100644 (file)
@@ -96,7 +96,7 @@ struct direction_chooser_args
     coord_def default_place;
 
     direction_chooser_args() :
-        hitfunc(NULL),
+        hitfunc(nullptr),
         restricts(DIR_NONE),
         mode(TARG_ANY),
         range(-1),
@@ -104,11 +104,11 @@ struct direction_chooser_args
         needs_path(true),
         may_target_monster(true),
         may_target_self(false),
-        target_prefix(NULL),
-        behaviour(NULL),
+        target_prefix(nullptr),
+        behaviour(nullptr),
         cancel_at_self(false),
         show_floor_desc(false),
-        get_desc_func(NULL),
+        get_desc_func(nullptr),
         default_place(0, 0) {}
 };
 
@@ -246,7 +246,7 @@ private:
     bool may_target_self;       // If true then player won't be prompted
     const char *target_prefix;  // A string displayed before describing target
     string top_prompt;          // Shown at the top of the message window
-    targeting_behaviour *behaviour; // Can be NULL for default
+    targeting_behaviour *behaviour; // Can be nullptr for default
     bool cancel_at_self;        // Disallow self-targeting?
     bool show_floor_desc;       // Describe the floor of the current target
     targetter *hitfunc;         // Determine what would be hit.
index 4a7d461..f008869 100644 (file)
@@ -183,7 +183,7 @@ int dlua_chunk::run(CLua &interp)
     if (err)
         return err;
     // callfn returns true on success, but we want to return 0 on success.
-    return check_op(interp, !interp.callfn(NULL, 0, 0));
+    return check_op(interp, !interp.callfn(nullptr, 0, 0));
 }
 
 int dlua_chunk::load_call(CLua &interp, const char *fn)
index fe23cd4..ea215d7 100644 (file)
@@ -108,7 +108,7 @@ static void _pick_float_exits(vault_placement &place,
                               vector<coord_def> &targets);
 static bool _feat_is_wall_floor_liquid(dungeon_feature_type);
 static bool _connect_spotty(const coord_def& from,
-                            bool (*overwriteable)(dungeon_feature_type) = NULL);
+                            bool (*overwriteable)(dungeon_feature_type) = nullptr);
 static bool _connect_vault_exit(const coord_def& exit);
 
 // VAULT FUNCTIONS
@@ -171,7 +171,7 @@ static dungeon_feature_type _pick_temple_altar(vault_placement &place);
 static dungeon_feature_type _pick_an_altar();
 
 static vector<god_type> _temple_altar_list;
-static CrawlHashTable*       _current_temple_hash = NULL; // XXX: hack!
+static CrawlHashTable*       _current_temple_hash = nullptr; // XXX: hack!
 
 // MISC FUNCTIONS
 static void _dgn_set_floor_colours();
@@ -190,7 +190,7 @@ vector<vault_placement> Temp_Vaults;
 static FixedBitVector<NUM_MONSTERS> temp_unique_creatures;
 static FixedVector<unique_item_status_type, MAX_UNRANDARTS> temp_unique_items;
 
-const map_bitmask *Vault_Placement_Mask = NULL;
+const map_bitmask *Vault_Placement_Mask = nullptr;
 
 static bool use_random_maps = true;
 static bool dgn_check_connectivity = false;
@@ -593,7 +593,7 @@ static void _dgn_map_colour_fixup()
                 env.grid_colours[x][y] = BLACK;
             }
 
-    dgn_colour_grid.reset(NULL);
+    dgn_colour_grid.reset(nullptr);
 }
 
 bool set_level_flags(uint32_t flags, bool silent)
@@ -741,7 +741,7 @@ static bool _dgn_fill_zone(
     const coord_def &start, int zone,
     point_record &record_point,
     bool (*passable)(const coord_def &) = _dgn_square_is_passable,
-    bool (*iswanted)(const coord_def &) = NULL)
+    bool (*iswanted)(const coord_def &) = nullptr)
 {
     bool ret = false;
     list<coord_def> points[2];
@@ -907,7 +907,7 @@ static int _process_disconnected_zones(int x1, int y1, int x2, int y2,
                                _dgn_square_is_passable,
                                choose_stairless ? (at_branch_bottom() ?
                                                    _is_upwards_exit_stair :
-                                                   _is_exit_stair) : NULL);
+                                                   _is_exit_stair) : nullptr);
 
             // If we want only stairless zones, screen out zones that did
             // have stairs.
@@ -1064,7 +1064,7 @@ dgn_register_place(const vault_placement &place, bool register_vault)
     {
         const keyed_mapspec *spec = place.map.mapspec_at(*vi - place.pos);
 
-        if (spec != NULL)
+        if (spec != nullptr)
         {
             env.level_map_mask(*vi) |= (short)spec->map_mask.flags_set;
             env.level_map_mask(*vi) &= ~((short)spec->map_mask.flags_unset);
@@ -1198,18 +1198,18 @@ void dgn_reset_level(bool enable_random_maps)
     env.level_build_method.clear();
     env.level_layout_types.clear();
     level_clear_vault_memory();
-    dgn_colour_grid.reset(NULL);
+    dgn_colour_grid.reset(nullptr);
 
     use_random_maps = enable_random_maps;
     dgn_check_connectivity = false;
     dgn_zones        = 0;
 
     _temple_altar_list.clear();
-    _current_temple_hash = NULL;
+    _current_temple_hash = nullptr;
 
     // Forget level properties.
     env.properties.clear();
-    env.heightmap.reset(NULL);
+    env.heightmap.reset(nullptr);
 
     env.absdepth0 = absdungeon_depth(you.where_are_you, you.depth);
 
@@ -1966,7 +1966,7 @@ static void _build_overflow_temples()
 
         const int num_gods = _setup_temple_altars(temple);
 
-        const map_def *vault = NULL;
+        const map_def *vault = nullptr;
         string vault_tag = "";
         string name = "";
 
@@ -1975,7 +1975,7 @@ static void _build_overflow_temples()
             name = temple[TEMPLE_MAP_KEY].get_string();
 
             vault = find_map_by_name(name);
-            if (vault == NULL)
+            if (vault == nullptr)
             {
                 mprf(MSGCH_ERROR,
                      "Couldn't find overflow temple map '%s'!",
@@ -2018,7 +2018,7 @@ static void _build_overflow_temples()
 
                 vault = random_map_for_tag(vault_tag, true);
 #ifdef DEBUG_TEMPLES
-                if (vault == NULL)
+                if (vault == nullptr)
                 {
                     mprf(MSGCH_DIAGNOSTICS, "Couldn't find overflow temple "
                          "for combination of tags %s", vault_tag.c_str());
@@ -2026,13 +2026,13 @@ static void _build_overflow_temples()
 #endif
             }
 
-            if (vault == NULL)
+            if (vault == nullptr)
             {
                 vault_tag = make_stringf("temple_overflow_generic_%d",
                                          num_gods);
 
                 vault = random_map_for_tag(vault_tag, true);
-                if (vault == NULL)
+                if (vault == nullptr)
                 {
                     mprf(MSGCH_ERROR,
                          "Couldn't find overflow temple tag '%s'!",
@@ -2041,7 +2041,7 @@ static void _build_overflow_temples()
             }
         }
 
-        if (vault == NULL)
+        if (vault == nullptr)
             // Might as well build the rest of the level if we couldn't
             // find the overflow temple map, so don't veto the level.
             return;
@@ -2064,7 +2064,7 @@ static void _build_overflow_temples()
              vault->name.c_str());
 #endif
     }
-    _current_temple_hash = NULL; // XXX: hack!
+    _current_temple_hash = nullptr; // XXX: hack!
 }
 
 struct coord_feat
@@ -2592,7 +2592,7 @@ static bool _vault_can_use_layout(const map_def *vault, const map_def *layout)
 
 static const map_def *_pick_layout(const map_def *vault)
 {
-    const map_def *layout = NULL;
+    const map_def *layout = nullptr;
 
     // This is intended for use with primary vaults, so...
     ASSERT(vault);
@@ -2662,7 +2662,7 @@ static bool _pan_level()
                                        + pandemon_level_names[which_demon]));
     }
 
-    const map_def *vault = NULL;
+    const map_def *vault = nullptr;
 
     if (which_demon >= 0)
     {
@@ -2741,7 +2741,7 @@ static const map_def *_dgn_random_map_for_place(bool minivault)
             || crawl_state.game_is_tutorial()))
     {
         vault = find_map_by_name(crawl_state.map);
-        if (vault == NULL)
+        if (vault == nullptr)
         {
             end(1, false, "Couldn't find selected map '%s'.",
                 crawl_state.map.c_str());
@@ -2778,7 +2778,7 @@ struct map_component
 
     map_component()
     {
-        min_equivalent = NULL;
+        min_equivalent = nullptr;
     }
     map_component * min_equivalent;
 
@@ -2794,7 +2794,7 @@ struct map_component
         max_coord = pos;
 
         label = in_label;
-        min_equivalent = NULL;
+        min_equivalent = nullptr;
     }
 
     void add_coord(const coord_def & pos)
@@ -2905,7 +2905,7 @@ static void _ccomps_8(FixedArray<int, GXM, GYM > & connectivity_map,
     // Reindex root labels, and move them to output
     for (auto &entry : intermediate_components)
     {
-        if (entry.second.min_equivalent == NULL)
+        if (entry.second.min_equivalent == nullptr)
         {
             entry.second.label = reindexed_label++;
             components.push_back(entry.second);
@@ -3140,7 +3140,7 @@ static void _place_chance_vaults()
 
 static void _place_minivaults()
 {
-    const map_def *vault = NULL;
+    const map_def *vault = nullptr;
     // First place the vault requested with &P
     if (you.props.exists("force_minivault")
         && (vault = find_map_by_name(you.props["force_minivault"])))
@@ -3632,7 +3632,7 @@ static void _place_branch_entrances(bool use_vaults)
 
             // Otherwise place a single stair feature.
             // Try to use designated locations for entrances if possible.
-            const coord_def portal_pos = find_portal_place(NULL, false);
+            const coord_def portal_pos = find_portal_place(nullptr, false);
             if (!portal_pos.origin())
             {
                 env.grid(portal_pos) = it->entry_stairs;
@@ -4070,7 +4070,7 @@ const vault_placement *dgn_place_map(const map_def *mdef,
                                      const coord_def &where)
 {
     if (!mdef)
-        return NULL;
+        return nullptr;
 
     const dgn_colour_override_manager colour_man;
 
@@ -4082,7 +4082,7 @@ const vault_placement *dgn_place_map(const map_def *mdef,
                  "Cannot generate encompass map '%s' with check_collision=true",
                  mdef->name.c_str());
 
-            return NULL;
+            return nullptr;
         }
 
         // For encompass maps, clear the entire level.
@@ -4160,7 +4160,7 @@ const vault_placement *dgn_safe_place_map(const map_def *mdef,
                 mdef = find_map_by_name(mapname);
             }
             else
-                return NULL;
+                return nullptr;
         }
     }
 }
@@ -4168,7 +4168,8 @@ const vault_placement *dgn_safe_place_map(const map_def *mdef,
 vault_placement *dgn_vault_at(coord_def p)
 {
     const int map_index = env.level_map_ids(p);
-    return map_index == INVALID_MAP_INDEX ? NULL : env.level_vaults[map_index];
+    return map_index == INVALID_MAP_INDEX ? nullptr
+                                          : env.level_vaults[map_index];
 }
 
 void dgn_seen_vault_at(coord_def p)
@@ -4256,7 +4257,7 @@ static const vault_placement *_build_vault_impl(const map_def *vault,
          place.pos.x, place.pos.y, place.size.x, place.size.y);
 
     if (placed_vault_orientation == MAP_NONE)
-        return NULL;
+        return nullptr;
 
     const bool is_layout = place.map.is_overwritable_layout();
 
@@ -5028,7 +5029,7 @@ static bool _dgn_place_one_monster(const vault_placement &place,
 
 /* "Oddball grids" are handled in _vault_grid. */
 static dungeon_feature_type _glyph_to_feat(int glyph,
-                                           vault_placement *place = NULL)
+                                           vault_placement *place = nullptr)
 {
     return (glyph == 'x') ? DNGN_ROCK_WALL :
            (glyph == 'X') ? DNGN_PERMAROCK_WALL :
@@ -5073,7 +5074,7 @@ dungeon_feature_type map_feature_at(map_def *map, const coord_def &c,
     if (rawfeat == ' ')
         return NUM_FEATURES;
 
-    keyed_mapspec *mapsp = map? map->mapspec_at(c) : NULL;
+    keyed_mapspec *mapsp = map? map->mapspec_at(c) : nullptr;
     if (mapsp)
     {
 &nb