diff --git a/Allure.cabal b/Allure.cabal
--- a/Allure.cabal
+++ b/Allure.cabal
@@ -1,11 +1,12 @@
 name:          Allure
-version:       0.4.12
-synopsis:      Near-future roguelike game in very early and active development
+version:       0.4.14
+synopsis:      Near-future roguelike game in early development
 description:   This is an alpha release of Allure of the Stars,
                a near-future Sci-Fi roguelike and tactical squad game.
-               The game is barely fun at this stage and not yet
-               really Sci-Fi. See the wiki for design notes and contribute.
+               See the wiki for design notes and contribute.
                .
+               <<GameDefinition/screenshot.png>>
+               .
                Please see the changelog file for recent improvements
                and the issue tracker for short-term plans. Long term goals
                are high replayability and auto-balancing through procedural
@@ -14,15 +15,16 @@
                .
                The game is written using the LambdaHack roguelike game engine
                available at <http://hackage.haskell.org/package/LambdaHack>.
-homepage:      http://github.com/Mikolaj/Allure
-bug-reports:   http://github.com/Mikolaj/Allure/issues
+homepage:      http://allureofthestars.com
+bug-reports:   http://github.com/AllureOfTheStars/Allure/issues
 license:       OtherLicense
 license-file:  LICENSE
 tested-with:   GHC == 7.6.3, GHC == 7.8
 data-files:    GameDefinition/config.ui.default,
                GameDefinition/scores
 extra-source-files: GameDefinition/PLAYING.md, GameDefinition/MainMenu.ascii,
-                    README.md, LICENSE, CREDITS, changelog, Makefile
+                    README.md, LICENSE, CREDITS, CHANGELOG.md, Makefile
+extra-doc-files: GameDefinition/screenshot.png
 author:        Andres Loeh, Mikolaj Konarski and others
 maintainer:    Mikolaj Konarski <mikolaj.konarski@funktory.com>
 category:      Game
@@ -31,25 +33,30 @@
 
 source-repository head
   type:               git
-  location:           git://github.com/Mikolaj/Allure.git
+  location:           git://github.com/AllureOfTheStars/Allure.git
 
 executable Allure
   hs-source-dirs:     GameDefinition
   main-is:            Main.hs
-  other-modules:      Content.ActorKind,
+  other-modules:      Client.UI.Content.KeyKind,
                       Content.CaveKind,
                       Content.FactionKind,
                       Content.ItemKind,
+                      Content.ItemKindActor,
+                      Content.ItemKindOrgan,
+                      Content.ItemKindShrapnel,
                       Content.ModeKind,
                       Content.PlaceKind,
                       Content.RuleKind,
                       Content.TileKind,
+                      TieKnot,
                       Paths_Allure
-  build-depends:      LambdaHack >= 0.2.12 && < 0.2.13,
+  build-depends:      LambdaHack >= 0.2.14 && <= 0.2.15,
                       template-haskell >= 2.6 && < 3,
 
                       array      >= 0.3.0.3 && < 1,
                       assert-failure >= 0.1 && < 1,
+                      async      >= 2       && < 3,
                       base       >= 4       && < 5,
                       binary     >= 0.7     && < 1,
                       bytestring >= 0.9.2   && < 1,
@@ -82,5 +89,49 @@
   ghc-options:        -Wall -fwarn-orphans -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -fwarn-monomorphism-restriction -fwarn-unrecognised-pragmas
   ghc-options:        -fno-warn-auto-orphans -fno-warn-implicit-prelude
   ghc-options:        -fno-ignore-asserts -funbox-strict-fields
-  ghc-options:        -threaded -with-rtsopts=-C0.005
+  ghc-options:        -threaded -with-rtsopts=-C0.005 -rtsopts
 -- ghc-options:       -with-rtsopts=-N  -- eats all cores
+
+test-suite test
+  type:               exitcode-stdio-1.0
+  hs-source-dirs:     GameDefinition, test
+  main-is:            test.hs
+  build-depends:      LambdaHack,
+                      template-haskell >= 2.6 && < 3,
+
+                      array      >= 0.3.0.3 && < 1,
+                      assert-failure >= 0.1 && < 1,
+                      async      >= 2       && < 3,
+                      base       >= 4       && < 5,
+                      binary     >= 0.7     && < 1,
+                      bytestring >= 0.9.2   && < 1,
+                      containers >= 0.5     && < 1,
+                      deepseq    >= 1.3     && < 2,
+                      directory  >= 1.1.0.1 && < 2,
+                      enummapset-th >= 0.6.0.0 && < 1,
+                      filepath   >= 1.2.0.1 && < 2,
+                      ghc-prim   >= 0.2,
+                      hashable   >= 1.1.2.5 && < 2,
+                      hsini      >= 0.2     && < 2,
+                      keys       >= 3       && < 4,
+                      miniutter  >= 0.4.1   && < 2,
+                      mtl        >= 2.0.1   && < 3,
+                      old-time   >= 1.0.0.7 && < 2,
+                      pretty-show >= 1.6    && < 2,
+                      random     >= 1.0.1   && < 2,
+                      stm        >= 2.4     && < 3,
+                      text       >= 0.11.2.3 && < 2,
+                      transformers >= 0.3   && < 1,
+                      unordered-containers >= 0.2.3 && < 1,
+                      vector     >= 0.10    && < 1,
+                      vector-binary-instances >= 0.2 && < 1,
+                      zlib       >= 0.5.3.1 && < 1
+
+  default-language:   Haskell2010
+  default-extensions: MonoLocalBinds, ScopedTypeVariables, OverloadedStrings
+                      BangPatterns, RecordWildCards, NamedFieldPuns
+  other-extensions:   TemplateHaskell
+  ghc-options:        -Wall -fwarn-orphans -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -fwarn-monomorphism-restriction -fwarn-unrecognised-pragmas
+  ghc-options:        -fno-warn-auto-orphans -fno-warn-implicit-prelude
+  ghc-options:        -fno-ignore-asserts -funbox-strict-fields
+  ghc-options:        -threaded -with-rtsopts=-C0.005 -rtsopts
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,59 @@
+## [v0.4.14, aka 'Out of cosmic balance'](https://github.com/AllureOfTheStars/Allure/compare/v0.4.12...v0.4.14)
+
+- add tons of new (unbalanced) items, actors and descriptions
+- add a simple cabal test in addition to make-test and travis-test
+- add items of Wonder and of Marvel
+- add game mechanics, items and places to enable stealthy tactics
+- add lots of shrapnel (explosions) and organs (body parts)
+- expose a bit of the plot via new game modes and their order
+
+## [v0.4.12](https://github.com/AllureOfTheStars/Allure/compare/v0.4.10...v0.4.12)
+
+- make walls lit by default to simplify exploration
+- improve and simplify dungeon generation
+- simplify running and permit multi-actor runs
+- let items explode and generate shrapnel projectiles
+- add game difficulty setting (initial HP scaling right now)
+- allow recording, playing back and looping commands
+- implement pathfinding via per-actor BFS over the whole level
+- extend setting targets for actors in UI tremendously
+- implement autoexplore, go-to-target, etc., as macros
+- let AI use pathfinding, switch leaders, pick levels to swarm to
+- force level/leader changes on spawners (even when played by humans)
+- extend and redesign UI bottom status lines
+
+## [v0.4.10](https://github.com/AllureOfTheStars/Allure/compare/v0.4.8...v0.4.10)
+
+- screensaver game modes (AI vs AI)
+- improved AI (can now climbs stairs, etc.)
+- multiple, multi-floor staircases
+- multiple savefiles
+- configurable framerate and combat animations
+
+## [v0.4.8](https://github.com/AllureOfTheStars/Allure/compare/v0.4.6.5...v0.4.8)
+
+- experimental multiplayer modes
+- a lot of gameplay changes induced by the engine overhaul and in particular the client-server rewrite
+
+## [v0.4.6.5](https://github.com/AllureOfTheStars/Allure/compare/v0.4.6...v0.4.6.5)
+
+- this is a minor release, primarily intended to fix the broken compilation on Hackage
+- changes since 0.4.6 are mostly unrelated to gameplay:
+    - strictly typed config files split into UI and rules
+    - a switch from Text to String throughout the codebase
+    - use of the external library miniutter for English sentence generation
+
+## [v0.4.6](https://github.com/AllureOfTheStars/Allure/compare/v0.4.4...v0.4.6)
+
+- the Main Menu
+- improved and configurable mode of squad combat
+
+## [v0.4.4](https://github.com/AllureOfTheStars/Allure/compare/v0.4.3...v0.4.4)
+
+- missiles flying for three turns (by an old kosmikus' idea)
+- visual feedback for targeting
+- animations of combat and individual monster moves
+
+## [v0.4.3](https://github.com/AllureOfTheStars/Allure/compare/v0.4.2...v0.4.3)
+
+- the Allure of the Stars game depends on the LambdaHack engine library
diff --git a/GameDefinition/Client/UI/Content/KeyKind.hs b/GameDefinition/Client/UI/Content/KeyKind.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Client/UI/Content/KeyKind.hs
@@ -0,0 +1,191 @@
+-- Copyright (c) 2008--2011 Andres Loeh, 2010--2014 Mikolaj Konarski
+-- This file is a part of the computer game Allure of the Stars
+-- and is released under the terms of the GNU Affero General Public License.
+-- For license and copyright information, see the file LICENSE.
+--
+-- | The default game key-command mapping to be used for UI. Can be overriden
+-- via macros in the config file.
+module Client.UI.Content.KeyKind ( standardKeys ) where
+
+import Control.Arrow (first)
+
+import qualified Game.LambdaHack.Client.Key as K
+import Game.LambdaHack.Client.UI.Content.KeyKind
+import Game.LambdaHack.Client.UI.HumanCmd
+import qualified Game.LambdaHack.Common.Effect as Effect
+import qualified Game.LambdaHack.Common.Feature as F
+import Game.LambdaHack.Common.Misc
+
+standardKeys :: KeyKind
+standardKeys = KeyKind
+  { rhumanCommands = map (first K.mkKM)
+      -- All commands are defined here, except some movement and leader picking
+      -- commands. All commands are shown on help screens except debug commands
+      -- and macros with empty descriptions.
+      -- The order below determines the order on the help screens.
+      -- Remember to put commands that show information (e.g., enter targeting
+      -- mode) first.
+
+      -- Main Menu, which apart of these includes a few extra commands
+      [ ("CTRL-x", ([CmdMenu], GameExit))
+      , ("CTRL-u", ([CmdMenu], GameRestart "duel"))
+      , ("CTRL-k", ([CmdMenu], GameRestart "skirmish"))
+      , ("CTRL-m", ([CmdMenu], GameRestart "ambush"))
+      , ("CTRL-b", ([CmdMenu], GameRestart "battle"))
+      , ("CTRL-a", ([CmdMenu], GameRestart "campaign"))
+      , ("CTRL-d", ([CmdMenu], GameDifficultyCycle))
+
+      -- Movement and terrain alteration
+      , ("less", ([CmdMove, CmdMinimal], TriggerTile
+           [ TriggerFeature { verb = "ascend"
+                            , object = "a level"
+                            , feature = F.Cause (Effect.Ascend 1) }
+           , TriggerFeature { verb = "escape"
+                            , object = "spaceship"
+                            , feature = F.Cause (Effect.Escape 1) } ]))
+      , ("CTRL-less", ([CmdMove], TriggerTile
+           [ TriggerFeature { verb = "ascend"
+                            , object = "10 levels"
+                            , feature = F.Cause (Effect.Ascend 10) } ]))
+      , ("greater", ([CmdMove, CmdMinimal], TriggerTile
+           [ TriggerFeature { verb = "descend"
+                            , object = "a level"
+                            , feature = F.Cause (Effect.Ascend (-1)) }
+           , TriggerFeature { verb = "escape"
+                            , object = "spaceship"
+                            , feature = F.Cause (Effect.Escape (-1)) } ]))
+      , ("CTRL-greater", ([CmdMove], TriggerTile
+           [ TriggerFeature { verb = "descend"
+                            , object = "10 levels"
+                            , feature = F.Cause (Effect.Ascend (-10)) } ]))
+      , ("semicolon", ([CmdMove], StepToTarget))
+      , ("colon", ([CmdMove], Macro "go to target for 100 steps"
+                                    ["semicolon", "V"]))
+      , ("CTRL-colon", ([CmdMove], Macro "go to target for 10 steps"
+                                         ["semicolon", "CTRL-V"]))
+      , ("x", ([CmdMove], Macro "explore the closest unknown spot"
+                                [ "BackSpace"
+                                , "CTRL-question", "semicolon", "V" ]))
+      , ("X", ([CmdMove], Macro "autoexplore 100 times"
+                                [ "BackSpace"
+                                , "'", "CTRL-question", "semicolon", "'"
+                                , "V" ]))
+      , ("CTRL-X", ([CmdMove], Macro "autoexplore 10 times"
+                                      [ "BackSpace"
+                                      , "'", "CTRL-question", "semicolon", "'"
+                                      , "CTRL-V" ]))
+      , ("R", ([CmdMove], Macro "rest (wait 100 times)"
+                                ["KP_Begin", "V"]))
+      , ("CTRL-R", ([CmdMove], Macro "rest (wait 10 times)"
+                                     ["KP_Begin", "CTRL-V"]))
+      , ("c", ([CmdMove], AlterDir
+           [ AlterFeature { verb = "close"
+                          , object = "door"
+                          , feature = F.CloseTo "vertical closed door Lit" }
+           , AlterFeature { verb = "close"
+                          , object = "door"
+                          , feature = F.CloseTo "horizontal closed door Lit" }
+           , AlterFeature { verb = "close"
+                          , object = "door"
+                          , feature = F.CloseTo "vertical closed door Dark" }
+           , AlterFeature { verb = "close"
+                          , object = "door"
+                          , feature = F.CloseTo "horizontal closed door Dark" }
+           ]))
+      , ("period", ([CmdMove], Macro "" ["KP_Begin"]))
+      , ("i", ([CmdMove], Macro "" ["KP_Begin"]))
+
+      -- Item use
+      --
+      -- For later:
+      -- ApplyItem {verb = "eat", object = "food", symbol = ','}
+      -- ApplyItem {verb = "activate", object = "emitter", symbol = '_'}
+      -- ApplyItem {verb = "use", object = "tool", symbol = '~'}
+      --
+      , ("E", ([CmdItem], DescribeItem CEqp))
+      , ("P", ([CmdItem], DescribeItem CInv))
+      , ("S", ([CmdItem], DescribeItem CSha))
+      , ("G", ([CmdItem], DescribeItem CGround))
+      , ("A", ([CmdItem], AllOwned))
+      , ("g", ([CmdItem, CmdMinimal],
+               MoveItem [CGround] CEqp "get" "an item" True))
+      , ("d", ([CmdItem], MoveItem [CEqp, CInv, CSha] CGround
+                                   "drop" "an item" False))
+      , ("e", ([CmdItem], MoveItem [CInv, CSha] CEqp
+                                   "equip" "an item" False))
+      , ("p", ([CmdItem], MoveItem [CEqp, CSha] CInv
+                                   "pack" "an item into inventory backpack"
+                                   False))
+      , ("s", ([CmdItem], MoveItem [CEqp, CInv] CSha
+                                   "stash" "and share an item" False))
+      , ("a", ([CmdItem, CmdMinimal], Apply
+           [ ApplyItem { verb = "activate"
+                       , object = "applicable item"
+                       , symbol = ' ' }
+           , ApplyItem { verb = "quaff"
+                       , object = "drink"
+                       , symbol = '!' }
+           , ApplyItem { verb = "read"
+                       , object = "tablet"
+                       , symbol = '?' }
+           ]))
+      , ("q", ([CmdItem], Apply [ApplyItem { verb = "quaff"
+                                           , object = "drink"
+                                           , symbol = '!' }]))
+      , ("r", ([CmdItem], Apply [ApplyItem { verb = "read"
+                                           , object = "tablet"
+                                           , symbol = '?' }]))
+      , ("f", ([CmdItem, CmdMinimal], Project
+           [ApplyItem { verb = "fling"
+                      , object = "projectable item"
+                      , symbol = ' ' }]))
+      , ("t", ([CmdItem], Project [ ApplyItem { verb = "throw"
+                                              , object = "missile"
+                                              , symbol = '}' }
+                                  , ApplyItem { verb = "throw"
+                                              , object = "missile"
+                                              , symbol = '{' } ]))
+      , ("z", ([CmdItem], Project [ApplyItem { verb = "zap"
+                                             , object = "mechanism"
+                                             , symbol = '-' }]))
+
+      -- Targeting
+      , ("KP_Multiply", ([CmdTgt, CmdMinimal], TgtEnemy))
+      , ("backslash", ([CmdTgt], Macro "" ["KP_Multiply"]))
+      , ("slash", ([CmdTgt], TgtFloor))
+      , ("plus", ([CmdTgt], EpsIncr True))
+      , ("minus", ([CmdTgt], EpsIncr False))
+      , ("BackSpace", ([CmdTgt], TgtClear))
+      , ("CTRL-question", ([CmdTgt], TgtUnknown))
+      , ("CTRL-I", ([CmdTgt], TgtItem))
+      , ("CTRL-braceleft", ([CmdTgt], TgtStair True))
+      , ("CTRL-braceright", ([CmdTgt], TgtStair False))
+
+      -- Automation
+      , ("equal", ([CmdAuto], SelectActor))
+      , ("underscore", ([CmdAuto], SelectNone))
+      , ("v", ([CmdAuto], Repeat 1))
+      , ("V", ([CmdAuto], Repeat 100))
+      , ("CTRL-v", ([CmdAuto], Repeat 1000))
+      , ("CTRL-V", ([CmdAuto], Repeat 10))
+      , ("apostrophe", ([CmdAuto], Record))
+      , ("CTRL-A", ([CmdAuto], Automate))
+
+      -- Assorted
+      , ("question", ([CmdMeta], Help))
+      , ("D", ([CmdMeta], History))
+      , ("T", ([CmdMeta], MarkSuspect))
+      , ("Z", ([CmdMeta], MarkVision))
+      , ("C", ([CmdMeta], MarkSmell))
+      , ("Tab", ([CmdMeta], MemberCycle))
+      , ("ISO_Left_Tab", ([CmdMeta], MemberBack))
+      , ("space", ([CmdMeta], Clear))
+      , ("Escape", ([CmdMeta, CmdMinimal], Cancel))
+      , ("Return", ([CmdMeta], Accept))
+
+      -- Debug and others not to display in help screens
+      , ("CTRL-s", ([CmdDebug], GameSave))
+      , ("CTRL-f", ([CmdDebug], GameRestart "safari"))
+      , ("CTRL-e", ([CmdDebug], GameRestart "defense"))
+      ]
+  }
diff --git a/GameDefinition/Content/ActorKind.hs b/GameDefinition/Content/ActorKind.hs
deleted file mode 100644
--- a/GameDefinition/Content/ActorKind.hs
+++ /dev/null
@@ -1,93 +0,0 @@
--- Copyright (c) 2008--2011 Andres Loeh, 2010--2014 Mikolaj Konarski
--- This file is a part of the computer game Allure of the Stars
--- and is released under the terms of the GNU Affero General Public License.
--- For license and copyright information, see the file LICENSE.
---
--- | Monsters and heroes for Allure of the Stars.
-module Content.ActorKind ( cdefs ) where
-
-import Game.LambdaHack.Common.Ability
-import Game.LambdaHack.Common.Color
-import Game.LambdaHack.Common.ContentDef
-import Game.LambdaHack.Common.Random
-import Game.LambdaHack.Common.Time
-import Game.LambdaHack.Content.ActorKind
-
-cdefs :: ContentDef ActorKind
-cdefs = ContentDef
-  { getSymbol = asymbol
-  , getName = aname
-  , getFreq = afreq
-  , validate = validateActorKind
-  , content =
-      [hero, projectile, eye, fastEye, nose]
-  }
-hero,        projectile, eye, fastEye, nose :: ActorKind
-
-hero = ActorKind
-  { asymbol = '@'
-  , aname   = "hero"
-  , afreq   = [("hero", 1)]
-  , acolor  = BrWhite  -- modified if many hero factions
-  , ahp     = rollDice 50 1
-  , aspeed  = toSpeed 2
-  , asight  = True
-  , asmell  = False
-  , aiq     = 15  -- higher that that leads to looping movement
-  , aregen  = 500
-  , acanDo  = [minBound..maxBound]
-  }
-
-projectile = ActorKind  -- includes homing missiles
-  { asymbol = '*'
-  , aname   = "projectile"
-  , afreq   = [("projectile", 1)]  -- Does not appear randomly in the dungeon.
-  , acolor  = BrWhite
-  , ahp     = rollDice 0 0
-  , aspeed  = toSpeed 0
-  , asight  = False
-  , asmell  = False
-  , aiq     = 0
-  , aregen  = maxBound
-  , acanDo  = [Track]
-  }
-
-eye = ActorKind
-  { asymbol = 'r'
-  , aname   = "deranged household robot"
-  , afreq   = [("robot", 60), ("horror", 60)]
-  , acolor  = BrYellow
-  , ahp     = rollDice 7 4
-  , aspeed  = toSpeed 2
-  , asight  = True
-  , asmell  = False
-  , aiq     = 16  -- leads to robotic, repetitious, looping movement
-  , aregen  = 100
-  , acanDo  = [minBound..maxBound]
-  }
-fastEye = ActorKind
-  { asymbol = 'm'
-  , aname   = "deformed monkey"
-  , afreq   = [("animal", 15), ("horror", 15)]
-  , acolor  = BrMagenta
-  , ahp     = rollDice 1 6
-  , aspeed  = toSpeed 4
-  , asight  = True
-  , asmell  = False
-  , aiq     = 12
-  , aregen  = 10  -- Regenerates fast (at max HP most of the time!).
-  , acanDo  = [minBound..maxBound]
-  }
-nose = ActorKind
-  { asymbol = 'h'
-  , aname   = "tentacled horror"
-  , afreq   = [("alien", 20), ("horror", 20)]
-  , acolor  = Green
-  , ahp     = rollDice 17 2
-  , aspeed  = toSpeed 1.8
-  , asight  = False
-  , asmell  = True
-  , aiq     = 0
-  , aregen  = 100
-  , acanDo  = [minBound..maxBound]
-  }
diff --git a/GameDefinition/Content/CaveKind.hs b/GameDefinition/Content/CaveKind.hs
--- a/GameDefinition/Content/CaveKind.hs
+++ b/GameDefinition/Content/CaveKind.hs
@@ -9,8 +9,8 @@
 import Data.Ratio
 
 import Game.LambdaHack.Common.ContentDef
+import Game.LambdaHack.Common.Dice
 import Game.LambdaHack.Common.Misc
-import Game.LambdaHack.Common.Random as Random
 import Game.LambdaHack.Content.CaveKind
 
 cdefs :: ContentDef CaveKind
@@ -20,30 +20,32 @@
   , getFreq = cfreq
   , validate = validateCaveKind
   , content =
-      [rogue, arena, empty, noise, combat, battle]
+      [rogue, arena, empty, noise, battle, skirmish, ambush, safari1, safari2, safari3]
   }
-rogue,        arena, empty, noise, combat, battle :: CaveKind
+rogue,        arena, empty, noise, battle, skirmish, ambush, safari1, safari2, safari3 :: CaveKind
 
 rogue = CaveKind
-  { csymbol       = '$'
+  { csymbol       = 'R'
   , cname         = "Storage area"
   , cfreq         = [("dng", 100), ("caveRogue", 1)]
   , cxsize        = fst normalLevelBound + 1
   , cysize        = snd normalLevelBound + 1
-  , cgrid         = rollDiceXY [(3, 2)] [(1, 2), (2, 1)]
-  , cminPlaceSize = rollDiceXY [(2, 2), (2, 1)] [(4, 1)]
-  , cmaxPlaceSize = rollDiceXY [(fst normalLevelBound, 1)]
-                               [(snd normalLevelBound, 1)]
-  , cdarkChance   = rollDeep (1, 54) (0, 0)
-  , cnightChance  = intToDeep 100
+  , cgrid         = DiceXY (3 * d 2) (d 2 + 2)
+  , cminPlaceSize = DiceXY (2 * d 2 + 2) 4
+  , cmaxPlaceSize = DiceXY 15 10
+  , cdarkChance   = d 54 + dl 20
+  , cnightChance  = 51
   , cauxConnects  = 1%3
   , cmaxVoid      = 1%6
   , cminStairDist = 30
   , cdoorChance   = 1%2
   , copenChance   = 1%10
   , chidden       = 8
-  , citemNum      = rollDice 7 2
-  , citemFreq     = [(70, "useful"), (30, "treasure")]
+  , cactorFreq    = [("alien", 33), ("animal", 33), ("robot", 33)]
+  , citemNum      = 10 * d 2
+  , citemFreq     = [("useful", 70), ("treasure", 30)]
+  , cplaceFreq    = [("rogue", 100)]
+  , cpassable     = False
   , cdefTile        = "fillerWall"
   , cdarkCorTile    = "floorCorridorDark"
   , clitCorTile     = "floorCorridorLit"
@@ -56,75 +58,124 @@
   { csymbol       = 'A'
   , cname         = "Recreational deck"
   , cfreq         = [("dng", 100), ("caveArena", 1)]
-  , cgrid         = rollDiceXY [(2, 2)] [(1, 2), (1, 1)]
-  , cminPlaceSize = rollDiceXY [(2, 2), (3, 1)] [(4, 1)]
-  , cdarkChance   = rollDeep (1, 80) (1, 60)
-  , cnightChance  = intToDeep 0
+  , cgrid         = DiceXY (2 * d 2) (2 * d 2)
+  , cminPlaceSize = DiceXY (2 * d 2 + 3) 4
+  , cdarkChance   = d 80 + dl 60
+  , cnightChance  = 0
   , cmaxVoid      = 1%3
   , chidden       = 1000
-  , citemNum      = rollDice 5 2  -- few rooms
+  , cactorFreq    = [("alien", 15), ("animal", 80), ("robot", 5)]
+  , citemNum      = 8 * d 2  -- few rooms
+  , cpassable     = True
   , cdefTile      = "arenaSet"
-  , cdarkCorTile  = "trailLit"  -- let paths around rooms be lit
+  , cdarkCorTile  = "trailLit"  -- let trails give off light
   , clitCorTile   = "trailLit"
-  , couterFenceTile = "oriels fence"
+-- TODO: re-add when less rooms , couterFenceTile = "oriels fence"
   }
 empty = rogue
-  { csymbol       = '.'
+  { csymbol       = 'E'
   , cname         = "Construction site"
   , cfreq         = [("dng", 100), ("caveEmpty", 1)]
-  , cgrid         = rollDiceXY [(1, 2), (1, 1)] [(1, 1)]
-  , cminPlaceSize = rollDiceXY [(10, 1)] [(10, 1)]
-  , cmaxPlaceSize = rollDiceXY [(fst normalLevelBound * 3 `div` 5, 1)]
-                               [(snd normalLevelBound * 3 `div` 5, 1)]
-  , cdarkChance   = rollDeep (1, 80) (1, 80)
-  , cnightChance  = intToDeep 0
+  , cgrid         = DiceXY (d 2 + 1) 1
+  , cminPlaceSize = DiceXY 10 10
+  , cmaxPlaceSize = DiceXY 24 12
+  , cdarkChance   = d 80 + dl 80
+  , cnightChance  = 0
   , cauxConnects  = 1
   , cmaxVoid      = 1%2
   , cminStairDist = 50
   , chidden       = 1000
-  , citemNum      = rollDice 8 2  -- whole floor strewn with treasure
+  , cactorFreq    = [("alien", 5), ("animal", 15), ("robot", 80)]
+  , citemNum      = 6 * d 2  -- few rooms
+  , cpassable     = True
   , cdefTile      = "emptySet"
-  , cdarkCorTile  = "trailLit"  -- let paths around rooms be lit
+  , cdarkCorTile  = "floorArenaDark"
   , clitCorTile   = "floorArenaLit"
   , couterFenceTile = "oriels fence"
   }
 noise = rogue
-  { csymbol       = '!'
+  { csymbol       = 'N'
   , cname         = "Machine rooms"
   , cfreq         = [("dng", 100), ("caveNoise", 1)]
-  , cgrid         = rollDiceXY [(2, 2)] [(1, 2), (1, 1)]
-  , cminPlaceSize = rollDiceXY [(3, 2), (2, 1)] [(5, 1)]
-  , cdarkChance   = rollDeep (1, 80) (1, 40)
-  , cnightChance  = rollDeep (1, 40) (1, 40)
+  , cgrid         = DiceXY 3 3
+  , cminPlaceSize = DiceXY 8 4
+  , cmaxPlaceSize = DiceXY 24 12
+  , cnightChance  = d 100
+  , cauxConnects  = 0
   , cmaxVoid      = 0
   , chidden       = 1000
-  , citemNum      = rollDice 4 2  -- fewer rooms
+  , cactorFreq    = [("alien", 80), ("animal", 5), ("robot", 15)]
+  , citemNum      = 12 * d 2  -- an incentive to explore the labyrinth
+  , cpassable     = True
+  , cplaceFreq    = [("noise", 50), ("rogue", 50)]
   , cdefTile      = "noiseSet"
+  , cdarkCorTile  = "floorArenaDark"
+  , clitCorTile   = "floorArenaLit"
+  }
+battle = rogue  -- few lights and many solids, to help the less numerous heroes
+  { csymbol       = 'B'
+  , cname         = "Ravaged spaceport"
+  , cfreq         = [("caveBattle", 1)]
+  , cgrid         = DiceXY (2 * d 2 + 1) 3
+  , cminPlaceSize = DiceXY 3 3
+  , cmaxPlaceSize = DiceXY 9 7
+  , cdarkChance   = 0
+  , cnightChance  = 100
+  , cdoorChance   = 2%10
+  , copenChance   = 9%10
+  , chidden       = 1000
+  , cactorFreq    = []
+  , citemNum      = 12 * d 2
+  , citemFreq     = [("useful", 100)]
+  , cplaceFreq    = [("battle", 50), ("rogue", 50)]
+  , cpassable     = True
+  , cdefTile      = "battleSet"
   , cdarkCorTile  = "trailLit"  -- let trails give off light
   , clitCorTile   = "trailLit"
   }
-combat = rogue
-  { csymbol       = 'C'
-  , cname         = "Combat arena"
-  , cfreq         = [("caveCombat", 1)]
-  , cgrid         = rollDiceXY [(2, 2), (3, 1)] [(1, 2), (2, 1)]
-  , cminPlaceSize = rollDiceXY [(3, 1)] [(3, 1)]
-  , cmaxPlaceSize = rollDiceXY [(5, 1)] [(5, 1)]
-  , cdarkChance   = intToDeep 100
-  , cnightChance  = rollDeep (1, 67) (0, 0)
-  , chidden       = 1000
-  , cauxConnects  = 0
+skirmish = rogue  -- many random solid tiles, to break LOS, since it's a day
+  { csymbol       = 'S'
+  , cname         = "Woodland biosphere"
+  , cfreq         = [("caveSkirmish", 1)]
+  , cgrid         = DiceXY (2 * d 2 + 2) (d 2 + 2)
+  , cminPlaceSize = DiceXY 3 3
+  , cmaxPlaceSize = DiceXY 7 5
+  , cdarkChance   = 100
+  , cnightChance  = 0
   , cdoorChance   = 1
   , copenChance   = 0
-  , citemNum      = rollDice 12 2
-  , citemFreq     = [(100, "useful")]
-  , cdefTile      = "combatSet"
-  , cdarkCorTile  = "trailLit"  -- let trails give off light
+  , chidden       = 1000
+  , cactorFreq    = []
+  , citemNum      = 12 * d 2
+  , citemFreq     = [("useful", 100)]
+  , cplaceFreq    = [("skirmish", 60), ("rogue", 40)]
+  , cpassable     = True
+  , cdefTile      = "skirmishSet"
+  , cdarkCorTile  = "floorArenaLit"
   , clitCorTile   = "floorArenaLit"
   }
-battle = combat  -- TODO: actors can get stuck forever among trees
-  { csymbol       = 'B'
-  , cname         = "Battle arena"
-  , cfreq         = [("caveBattle", 1)]
-  , cdefTile      = "battleSet"
+ambush = rogue  -- lots of lights, to give a chance to snipe
+  { csymbol       = 'M'
+  , cname         = "Public garden"
+  , cfreq         = [("caveAmbush", 1)]
+  , cgrid         = DiceXY (2 * d 2 + 3) (d 2 + 2)
+  , cminPlaceSize = DiceXY 3 3
+  , cmaxPlaceSize = DiceXY 5 5
+  , cdarkChance   = 0
+  , cnightChance  = 100
+  , cauxConnects  = 1
+  , cdoorChance   = 1%10
+  , copenChance   = 9%10
+  , chidden       = 1000
+  , cactorFreq    = []
+  , citemNum      = 12 * d 2
+  , citemFreq     = [("useful", 100)]
+  , cplaceFreq    = [("ambush", 100)]
+  , cpassable     = True
+  , cdefTile      = "ambushSet"
+  , cdarkCorTile  = "trailLit"  -- let trails give off light
+  , clitCorTile   = "trailLit"
   }
+safari1 = ambush {cfreq = [("caveSafari1", 1)]}
+safari2 = battle {cfreq = [("caveSafari2", 1)]}
+safari3 = skirmish {cfreq = [("caveSafari3", 1)]}
diff --git a/GameDefinition/Content/FactionKind.hs b/GameDefinition/Content/FactionKind.hs
--- a/GameDefinition/Content/FactionKind.hs
+++ b/GameDefinition/Content/FactionKind.hs
@@ -7,7 +7,7 @@
 -- for Allure of the Stars.
 module Content.FactionKind ( cdefs ) where
 
-import Data.List
+import qualified Data.EnumMap.Strict as EM
 
 import Game.LambdaHack.Common.Ability
 import Game.LambdaHack.Common.ContentDef
@@ -20,65 +20,71 @@
   , getFreq = ffreq
   , validate = validateFactionKind
   , content =
-      [hero, alien, animal, robot, horror]
+      [hero, civilian, alien, animal, robot, horror]
   }
-hero,        alien, animal, robot, horror :: FactionKind
+hero,        civilian, alien, animal, robot, horror :: FactionKind
 
 hero = FactionKind
-  { fsymbol        = '@'
-  , fname          = "hero"
-  , ffreq          = [("hero", 1)]
-  , fAbilityLeader = allAbilities
-  , fAbilityOther  = meleeAdjacent
+  { fsymbol       = '1'
+  , fname         = "hero"
+  , ffreq         = [("hero", 1)]
+  , fSkillsLeader = allSkills
+  , fSkillsOther  = meleeAdjacent
   }
 
+civilian = FactionKind
+  { fsymbol       = '@'
+  , fname         = "civilian"
+  , ffreq         = [("civilian", 1)]
+  , fSkillsLeader = allSkills
+  , fSkillsOther  = allSkills  -- not coordinated by any leadership
+  }
+
 -- Includes alien-operated robots, alien-conditioned animals and hybrids.
 alien = FactionKind
-  { fsymbol        = 'a'
-  , fname          = "alien"
-  , ffreq          = [("alien", 1), ("summon", 20)]
-  , fAbilityLeader = allAbilities
-  , fAbilityOther  = allAbilities
+  { fsymbol       = 'a'
+  , fname         = "alien"
+  , ffreq         = [("alien", 1)]
+  , fSkillsLeader = allSkills
+  , fSkillsOther  = allSkills
   }
 
 animal = FactionKind
-  { fsymbol        = 'd'
-  , fname          = "animal"
-  , ffreq          = [("animal", 1), ("summon", 50)]
-  , fAbilityLeader = animalAbility
-  , fAbilityOther  = animalAbility
+  { fsymbol       = 'd'
+  , fname         = "animal"
+  , ffreq         = [("animal", 1)]
+  , fSkillsLeader = animalSkills
+  , fSkillsOther  = animalSkills
   }
 
 -- Autonomous robots.
 robot = FactionKind
-  { fsymbol        = 'r'
-  , fname          = "robot"
-  , ffreq          = [("robot", 1), ("summon", 10)]
-  , fAbilityLeader = robotAbility
-  , fAbilityOther  = robotAbility
+  { fsymbol       = 'r'
+  , fname         = "robot"
+  , ffreq         = [("robot", 1)]
+  , fSkillsLeader = robotSkills
+  , fSkillsOther  = robotSkills
   }
 
 horror = FactionKind
-  { fsymbol        = 'h'
-  , fname          = "horror"
-  , ffreq          = [("horror", 1), ("summon", 50)]
-  , fAbilityLeader = allAbilities
-  , fAbilityOther  = allAbilities
+  { fsymbol       = 'h'
+  , fname         = "horror"
+  , ffreq         = [("horror", 1)]
+  , fSkillsLeader = allSkills
+  , fSkillsOther  = allSkills
   }
 
 
-_noAbility, _onlyFollowTrack, meleeAdjacent, _meleeAndRanged, animalAbility, robotAbility, allAbilities :: [Ability]
-
-_noAbility = []  -- not even projectiles will fly
-
-_onlyFollowTrack = [Track]  -- projectiles enabled
+meleeAdjacent, _meleeAndRanged, animalSkills, robotSkills, allSkills :: Skills
 
-meleeAdjacent = [Track, Melee]
+meleeAdjacent = EM.fromList $ zip [AbWait, AbMelee] [1, 1..]
 
-_meleeAndRanged = [Track, Melee, Ranged]  -- melee and reaction fire
+-- Melee and reaction fire.
+_meleeAndRanged = EM.fromList $ zip [AbWait, AbMelee, AbProject] [1, 1..]
 
-animalAbility = [Track, Flee, Melee, Displace, Chase, Wander]
+animalSkills =
+  EM.fromList $ zip [AbMove, AbMelee, AbAlter, AbWait, AbTrigger] [1, 1..]
 
-robotAbility = delete Flee [minBound..maxBound]
+robotSkills = EM.delete AbApply unitSkills
 
-allAbilities = [minBound..maxBound]
+allSkills = unitSkills
diff --git a/GameDefinition/Content/ItemKind.hs b/GameDefinition/Content/ItemKind.hs
--- a/GameDefinition/Content/ItemKind.hs
+++ b/GameDefinition/Content/ItemKind.hs
@@ -3,17 +3,20 @@
 -- and is released under the terms of the GNU Affero General Public License.
 -- For license and copyright information, see the file LICENSE.
 --
--- | Weapons and treasure for Allure of the Stars.
+-- | Weapon and treasure definitions.
 module Content.ItemKind ( cdefs ) where
 
-import qualified Data.List as L
+import Data.List
 
+import Content.ItemKindActor
+import Content.ItemKindOrgan
+import Content.ItemKindShrapnel
 import Game.LambdaHack.Common.Color
 import Game.LambdaHack.Common.ContentDef
+import Game.LambdaHack.Common.Dice
 import Game.LambdaHack.Common.Effect
 import Game.LambdaHack.Common.Flavour
-import Game.LambdaHack.Common.ItemFeature
-import Game.LambdaHack.Common.Random
+import Game.LambdaHack.Common.Misc
 import Game.LambdaHack.Content.ItemKind
 
 cdefs :: ContentDef ItemKind
@@ -22,15 +25,17 @@
   , getName = iname
   , getFreq = ifreq
   , validate = validateItemKind
-  , content =
-      [necklace, dart, gem1, gem2, gem3, currency, javelin, kitchenKnife, potion1, potion2, potion3, ring, scroll1, scroll2, scroll3, sword, wand1, wand2, fist, foot, tentacle, fragrance, mist_healing, mist_wounding, glass_piece, smoke]
+  , content = items ++ organs ++ shrapnels ++ actors
   }
-necklace,        dart, gem1, gem2, gem3, currency, javelin, kitchenKnife, potion1, potion2, potion3, ring, scroll1, scroll2, scroll3, sword, wand1, wand2, fist, foot, tentacle, fragrance, mist_healing, mist_wounding, glass_piece, smoke :: ItemKind
 
-gem, potion, scroll, wand :: ItemKind  -- generic templates
+items :: [ItemKind]
+items =
+  [canOfGlue, crankSpotlight, buckler, dart, dart200, gem1, gem2, gem3, gloveFencing, gloveGauntlet, gloveJousting, currency, gorget, harpoon, jumpingPole, contactLens, necklace1, necklace2, necklace3, necklace4, necklace5, necklace6, necklace7, net, needle, oilLamp, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, ring1, ring2, ring3, ring4, ring5, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, shield, dagger, hammer, sword, halberd, wand1, wand2, candle, armorLeather, armorMail, honingSteel, constructionHooter]
 
--- castDeep (aDb, xDy) = castDice aDb + (lvl - 1) * castDice xDy / (depth - 1)
+canOfGlue,    crankSpotlight, buckler, dart, dart200, gem1, gem2, gem3, gloveFencing, gloveGauntlet, gloveJousting, currency, gorget, harpoon, jumpingPole, contactLens, necklace1, necklace2, necklace3, necklace4, necklace5, necklace6, necklace7, net, needle, oilLamp, potion1, potion2, potion3, potion4, potion5, potion6, potion7, ring1, potion8, potion9, potion10, ring2, ring3, ring4, ring5, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, shield, dagger, hammer, sword, halberd, wand1, wand2, candle, armorLeather, armorMail, honingSteel, constructionHooter :: ItemKind
 
+gem, necklace, potion, ring, scroll, wand :: ItemKind  -- generic templates
+
 {- Item group symbols (from Angband, only as an informal convention for now):
 
 ! potion, flask, concoction, bottle, jar, vial, canister
@@ -54,256 +59,690 @@
 
 -}
 
-necklace = ItemKind
-  { isymbol  = '"'
-  , iname    = "necklace"
-  , ifreq    = [("useful", 6)]
-  , iflavour = zipFancy [BrGreen]
-  , icount   = intToDeep 1
-  , iverbApply   = "tear down"
-  , iverbProject = "cast"
-  , iweight  = 30
-  , itoThrow = -50  -- not dense enough
-  , ifeature = [Cause $ Regeneration (rollDeep (2, 3) (1, 10))]
-  }
+-- * Thrown weapons
+
 dart = ItemKind
   { isymbol  = '{'
+  , iname    = "steak knife"
+  , ifreq    = [("useful", 100), ("any arrow", 100)]
+  , iflavour = zipPlain [BrCyan]
+  , icount   = 3 * d 3
+  , irarity  = [(1, 20)]
+  , iverbHit = "prick"
+  , iweight  = 100
+  , iaspects = [AddHurtRanged ((d 6 + dl 6) |*| 10)]
+  , ieffects = [Hurt (3 * d 1)]
+  , ifeature = [toVelocity 75]  -- no fins no special balance
+  , idesc    = "Not particularly well balanced, but with a laser-sharpened titanium tip and blade."
+  , ikit     = []
+  }
+dart200 = ItemKind
+  { isymbol  = '{'
   , iname    = "billiard ball"
-  , ifreq    = [("useful", 20)]
+  , ifreq    = [("useful", 100), ("any arrow", 50)]  -- TODO: until arrows added
   , iflavour = zipPlain [BrWhite]
-  , icount   = rollDeep (3, 3) (0, 0)
-  , iverbApply   = "splinter"
-  , iverbProject = "hurl"
-  , iweight  = 50
-  , itoThrow = 0  -- a cheap dart
-  , ifeature = [Cause $ Hurt (rollDice 1 2) (rollDeep (1, 2) (1, 2))]
+  , icount   = 3 * d 3
+  , irarity  = [(4, 20)]
+  , iverbHit = "strike"
+  , iweight  = 300
+  , iaspects = [AddHurtRanged ((d 6 + dl 6) |*| 10)]
+  , ieffects = [Hurt (2 * d 1)]
+  , ifeature = [toVelocity 150]
+  , idesc    = "Ideal shape, size and weight for throwing."
+  , ikit     = []
   }
+
+-- * Exotic thrown weapons
+
+canOfGlue = ItemKind
+  { isymbol  = '{'
+  , iname    = "can of glue"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [Magenta]
+  , icount   = dl 4
+  , irarity  = [(5, 5), (10, 20)]
+  , iverbHit = "glue"
+  , iweight  = 1500
+  , iaspects = []
+  , ieffects = [Paralyze (5 + d 10)]
+  , ifeature = [toVelocity 50]  -- unwieldy
+  , idesc    = "A can of liquid, fast-setting, construction glue."
+  , ikit     = []
+  }
+harpoon = ItemKind
+  { isymbol  = '{'
+  , iname    = "harpoon"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [Brown]
+  , icount   = dl 5
+  , irarity  = [(5, 3), (10, 5)]
+  , iverbHit = "hook"
+  , iweight  = 4000
+  , iaspects = [AddHurtRanged ((d 2 + 2 * dl 5) |*| 10)]
+  , ieffects = [Hurt (4 * d 1), PullActor (ThrowMod 200 50)]
+  , ifeature = []
+  , idesc    = "A display piece harking back to the Earth's oceanic tourism hayday. The cruel, barbed head lodges in its victim so painfully that the weakest tug of the thin line sends the victim flying."
+  , ikit     = []
+  }
+net = ItemKind
+  { isymbol  = '{'
+  , iname    = "net"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [White]
+  , icount   = dl 3
+  , irarity  = [(3, 5), (10, 4)]
+  , iverbHit = "entangle"
+  , iweight  = 1000
+  , iaspects = []
+  , ieffects = [ Paralyze (5 + d 5)
+               , DropBestWeapon, DropEqp ')' False ]
+  , ifeature = []
+  , idesc    = "A large synthetic fibre net with weights affixed along the edges. Entangles weapon and appendages alike."
+  , ikit     = []
+  }
+needle = ItemKind
+  { isymbol  = '{'
+  , iname    = "needle"
+  , ifreq    = []
+  , iflavour = zipPlain [BrBlue]
+  , icount   = 9 * d 3
+  , irarity  = []
+  , iverbHit = "prick"
+  , iweight  = 1
+  , iaspects = [AddHurtRanged ((d 3 + dl 3) |*| 10)]
+  , ieffects = [Hurt (1 * d 1)]
+  , ifeature = [toVelocity 200, Fragile]
+  , idesc    = "The hypodermic needle part of a micro-syringe. Without the payload, it flies far and penetrates deeply, causing intense pain on movement."
+  , ikit     = []
+  }
+
+-- * Lights
+
+candle = ItemKind
+  { isymbol  = '~'
+  , iname    = "candle"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [Brown]
+  , icount   = 1
+  , irarity  = [(1, 10)]
+  , iverbHit = "scorch"
+  , iweight  = 500
+  , iaspects = [ AddLight 3
+               , AddSight (-2) ]
+  , ieffects = [Burn 3]
+  , ifeature = [ toVelocity 50  -- easy to break when throwing
+               , Fragile, EqpSlot EqpSlotAddLight "", Identified ]
+  , idesc    = "A smoking, thick candle with an unsteady fire."
+  , ikit     = []
+  }
+oilLamp = ItemKind
+  { isymbol  = '~'
+  , iname    = "oil lamp"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [BrYellow]
+  , icount   = 1
+  , irarity  = [(5, 4), (10, 4)]
+  , iverbHit = "burn"
+  , iweight  = 1000
+  , iaspects = [AddLight 3, AddSight (-1)]
+  , ieffects = [Burn 3, Paralyze 3, OnSmash (Explode "burning oil 3")]
+  , ifeature = [ toVelocity 70  -- hard not to spill the oil while throwing
+               , Fragile, EqpSlot EqpSlotAddLight "", Identified ]
+  , idesc    = "A sizable glass lamp filled with plant oil feeding a wick."
+  , ikit     = []
+  }
+crankSpotlight = ItemKind
+  { isymbol  = '~'
+  , iname    = "crank spotlight"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [BrWhite]
+  , icount   = 1
+  , irarity  = [(10, 2)]
+  , iverbHit = "snag"
+  , iweight  = 2400
+  , iaspects = [AddLight 4, AddArmorRanged $ - d 3]  -- noise and busy hands
+  , ieffects = []
+  , ifeature = [ EqpSlot EqpSlotAddLight "", Identified ]
+  , idesc    = "Powerful, wide-beam spotlight, powered by a hand-crank. Requires noisy two-handed recharging every few minutes."
+  , ikit     = []
+  }
+
+-- * Treasure
+
 gem = ItemKind
   { isymbol  = '*'
-  , iname    = "precious gem"
-  , ifreq    = [("treasure", 20)]  -- x3, but rare on shallow levels
-  , iflavour = zipPlain $ L.delete BrYellow brightCol  -- natural, so not fancy
-  , icount   = intToDeep 0
-  , iverbApply   = "crush"
-  , iverbProject = "toss"
+  , iname    = "gem"
+  , ifreq    = [("treasure", 100)]  -- x3, but rare on shallow levels
+  , iflavour = zipPlain $ delete BrYellow brightCol  -- natural, so not fancy
+  , icount   = 1
+  , irarity  = []
+  , iverbHit = "tap"
   , iweight  = 50
-  , itoThrow = 0
-  , ifeature = []
+  , iaspects = [AddLight 1, AddSpeed (-1)]  -- reflects strongly, distracts
+  , ieffects = []
+  , ifeature = [ Durable  -- prevent destruction by evil monsters
+               , Precious ]
+  , idesc    = "Precious, though useless. Worth around 100 gold grains."
+  , ikit     = []
   }
 gem1 = gem
-  { icount   = rollDeep (0, 0) (1, 1)  -- appears on max depth
+  { irarity  = [(2, 0), (10, 10)]
   }
 gem2 = gem
-  { icount   = rollDeep (0, 0) (1, 2)  -- appears halfway
+  { irarity  = [(5, 0), (10, 10)]
   }
 gem3 = gem
-  { icount   = rollDeep (0, 0) (1, 3)  -- appears early
+  { irarity  = [(8, 0), (10, 10)]
   }
 currency = ItemKind
   { isymbol  = '$'
   , iname    = "gold grain"
-  , ifreq    = [("treasure", 20), ("currency", 1)]
+  , ifreq    = [("treasure", 100), ("currency", 1)]
   , iflavour = zipPlain [BrYellow]
-  , icount   = rollDeep (0, 0) (10, 10)  -- appears on lvl 2
-  , iverbApply   = "smear"
-  , iverbProject = "blow away"
+  , icount   = 10 + d 20 + dl 20
+  , irarity  = [(1, 0), (5, 20), (10, 10)]
+  , iverbHit = "tap"
   , iweight  = 1
-  , itoThrow = 0
-  , ifeature = []
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [Durable, Identified, Precious]
+  , idesc    = "Reliably valuable in every civilized place."
+  , ikit     = []
   }
-javelin = ItemKind
-  { isymbol  = '{'
-  , iname    = "javelin"
-  , ifreq    = [("useful", 30)]
-  , iflavour = zipPlain [Brown]
-  , icount   = rollDeep (0, 0) (1, 1)
-  , iverbApply   = "break up"
-  , iverbProject = "hurl"
-  , iweight  = 2000
-  , itoThrow = 0  -- cheap but deadly
-  , ifeature = [Cause $ Hurt (rollDice 2 2) (rollDeep (1, 2) (2, 2))]
+
+-- * Periodic jewelry
+
+gorget = ItemKind
+  { isymbol  = '"'
+  , iname    = "gorget"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipFancy [BrCyan]
+  , irarity  = [(4, 1), (10, 2)]
+  , icount   = 1
+  , iverbHit = "whip"
+  , iweight  = 30
+  , iaspects = [Periodic $ d 4 + dl 4, AddArmorMelee 1, AddArmorRanged 1]
+  , ieffects = [RefillCalm 1]
+  , ifeature = [ Precious, EqpSlot EqpSlotPeriodic "", Identified
+               , toVelocity 50 ]  -- not dense enough
+  , idesc    = "Highly ornamental, cold, large, steel medallion on a chain. Unlikely to offer much protection as an armor piece, but the old, worn engraving reassures you."
+  , ikit     = []
   }
-kitchenKnife = ItemKind
-  { isymbol  = '{'
-  , iname    = "kitchen knife"
-  , ifreq    = [("useful", 25)]
-  , iflavour = zipPlain [BrCyan]
-  , icount   = rollDeep (0, 0) (1, 2)
-  , iverbApply   = "bend"
-  , iverbProject = "throw"
-  , iweight  = 200
-  , itoThrow = -50  -- too flexible and the handle causes air resistance
-  , ifeature = [Cause $ Hurt (rollDice 1 1) (rollDeep (0, 0) (1, 2))]
+necklace = ItemKind
+  { isymbol  = '"'
+  , iname    = "necklace"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipFancy stdCol ++ zipPlain brightCol
+  , irarity  = [(4, 2), (10, 5)]
+  , icount   = 1
+  , iverbHit = "whip"
+  , iweight  = 30
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [ Precious, EqpSlot EqpSlotPeriodic ""
+               , toVelocity 50 ]  -- not dense enough
+  , idesc    = "Tingling, rattling chain of flat encrusted links. Eccentric millionaires are known to hide their highly personalized body augmentation packs in such large jewelry pieces."
+  , ikit     = []
   }
+necklace1 = necklace
+  { iaspects = [Periodic $ d 2 + dl 2]
+  , ieffects = [RefillHP 1]
+  }
+necklace2 = necklace
+  { irarity  = [(2, 0), (10, 1)]
+  , iaspects = [Periodic $ d 4 + dl 2]
+  , ieffects = [ Impress
+               , Summon [("summonable animal", 1)] $ 1 + dl 2, Explode "waste" ]
+  }
+necklace3 = necklace
+  { iaspects = [Periodic $ d 4 + dl 2]
+  , ieffects = [Paralyze $ 5 + d 5 + dl 5, RefillCalm 999]
+  }
+necklace4 = necklace
+  { iaspects = [Periodic $ 2 * d 10 + dl 10]
+  , ieffects = [Teleport $ 2 + d 3]
+  }
+necklace5 = necklace
+  { iaspects = [Periodic $ d 4 + dl 2]
+  , ieffects = [Teleport $ 10 + d 10]
+  }
+necklace6 = necklace
+  { iaspects = [Periodic $ 2 * d 5 + dl 5]
+  , ieffects = [PushActor (ThrowMod 100 50)]
+  }
+necklace7 = necklace
+  { irarity  = [(4, 0), (10, 2)]
+  , iaspects = [Periodic $ 2 * d 5 + dl 15]
+  , ieffects = [InsertMove 1, RefillHP (-1)]
+  , ifeature = ifeature necklace ++ [Durable]
+                 -- evil players would throw before death, to destroy
+      -- TODO: teach AI to wear only for fight; prevent players from meleeing
+      -- allies with that (Durable)
+  }
+
+-- * Non-periodic jewelry
+
+contactLens = ItemKind
+  { isymbol  = '='
+  , iname    = "contact lens"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [White]
+  , icount   = 1
+  , irarity  = [(6, 0), (10, 1)]
+  , iverbHit = "rap"
+  , iweight  = 50
+  , iaspects = [AddSight $ dl 3]
+  , ieffects = []
+  , ifeature = [Precious, Identified, Durable, EqpSlot EqpSlotAddSight ""]
+  , idesc    = "Advanced design. Never needs to be taken off."
+  , ikit     = []
+  }
+ring = ItemKind
+  { isymbol  = '='
+  , iname    = "ring"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain stdCol ++ zipFancy darkCol
+  , icount   = 1
+  , irarity  = [(6, 2), (10, 5)]
+  , iverbHit = "knock"
+  , iweight  = 15
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [Precious, Identified]
+  , idesc    = "A sturdy ring with a softly shining eye. If it contains a body booster unit, beware of the side-effects."
+  , ikit     = []
+  }
+ring1 = ring
+  { irarity  = [(2, 0), (10, 2)]
+  , iaspects = [AddSpeed 1, AddMaxHP $ dl 3 - 5 - d 3]
+  , ifeature = ifeature ring ++ [Durable, EqpSlot EqpSlotAddSpeed ""]
+  }
+ring2 = ring
+  { iaspects = [AddMaxHP $ 3 + dl 5, AddMaxCalm $ dl 6 - 15 - d 6]
+  , ifeature = ifeature ring ++ [EqpSlot EqpSlotAddMaxHP ""]
+  }
+ring3 = ring
+  { iaspects = [AddMaxCalm $ 10 + dl 10]
+  , ifeature = ifeature ring ++ [EqpSlot EqpSlotAddMaxCalm ""]
+  , idesc    = "Cold, solid to the touch, perfectly round, engraved with solemn, strangely comforting, worn out words."
+  }
+ring4 = ring  -- TODO: move to level-ups and to timed effects
+  { irarity  = [(3, 8), (10, 12)]
+  , iaspects = [AddHurtMelee $ 3 * d 4 + dl 15, AddMaxHP $ dl 3 - 4 - d 2]
+  , ifeature = ifeature ring ++ [Durable, EqpSlot EqpSlotAddHurtMelee ""]
+  }
+ring5 = ring  -- by the time it's found, probably no space in eqp
+  { irarity  = [(5, 0), (10, 1)]
+  , iaspects = [AddLight $ d 2]
+  , ifeature = ifeature ring ++ [EqpSlot EqpSlotAddLight ""]
+  }
+
+-- * Exploding consumables, often intended to be thrown
+
 potion = ItemKind
   { isymbol  = '!'
   , iname    = "vial"
-  , ifreq    = [("useful", 15)]
-  , iflavour = zipFancy stdCol
-  , icount   = intToDeep 1
-  , iverbApply   = "gulp down"
-  , iverbProject = "lob"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain stdCol ++ zipFancy brightCol
+  , icount   = 1
+  , irarity  = [(1, 10), (10, 8)]
+  , iverbHit = "splash"
   , iweight  = 200
-  , itoThrow = -50  -- oily, bad grip
-  , ifeature = []
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [ toVelocity 50  -- oily, bad grip
+               , Applicable, Fragile ]
+  , idesc    = "A flask of bubbly, slightly oily liquid of a suspect color."  -- purely natural; no nano, no alien tech  -- TODO: move distortion to a special flask item or trigger when some precious high tech item is destroyed (jewelry?)?
+  , ikit     = []
   }
 potion1 = potion
-  { ifreq    = [("useful", 5)]
-  , ifeature = [Cause ApplyPerfume, Explode "fragrance"]
+  { ieffects = [ NoEffect "rose water", Impress
+               , OnSmash (ApplyPerfume), OnSmash (Explode "fragrance") ]
   }
 potion2 = potion
-  { ifeature = [Cause $ Heal 5, Explode "mist healing"]
+  { irarity  = [(10, 1)]
+  , ieffects = [ NoEffect "musky concoction", Impress, DropBestWeapon
+               , OnSmash (Explode "pheromone")]
   }
 potion3 = potion
-  { ifreq    = [("useful", 5)]
-  , ifeature = [Cause $ Heal (-5), Explode "mist wounding"]
+  { ieffects = [RefillHP 5, OnSmash (Explode "healing mist")]
   }
-ring = ItemKind
-  { isymbol  = '='
-  , iname    = "ring"
-  , ifreq    = []  -- [("useful", 10)]  -- TODO: make it useful
-  , iflavour = zipPlain [White]
-  , icount   = intToDeep 1
-  , iverbApply   = "squeeze down"
-  , iverbProject = "toss"
-  , iweight  = 15
-  , itoThrow = 0
-  , ifeature = [Cause $ Searching (rollDeep (1, 6) (3, 2))]
+potion4 = potion  -- TODO: a bit boring
+  { irarity  = [(1, 5)]
+  , ieffects = [RefillHP (-5), OnSmash (Explode "wounding mist")]
   }
+potion5 = potion
+  { ieffects = [ Explode "explosion blast 10"
+               , PushActor (ThrowMod 200 75)
+               , OnSmash (Explode "explosion blast 10") ]
+  }
+potion6 = potion
+  { irarity  = [(10, 2)]
+  , ieffects = [ NoEffect "distortion", Impress
+               , OnSmash (Explode "distortion")]
+  }
+potion7 = potion
+  { ieffects = [ NoEffect "bait cocktail", Impress
+               , OnSmash (Summon [("summonable animal", 1)] $ 1 + dl 2)
+               , OnSmash (Explode "waste") ]
+  }
+potion8 = potion
+  { ieffects = [ OneOf [Impress, DropBestWeapon, RefillHP 5, Burn 3]
+               , OnSmash (OneOf [ Explode "healing mist"
+                                , Explode "wounding mist"
+                                , Explode "fragrance"
+                                , Explode "explosion blast 10" ]) ]
+  }
+potion9 = potion
+  { irarity  = [(4, 1), (10, 2)]
+  , ieffects = [ OneOf [ Dominate, DropBestWeapon, RefillHP 15, Burn 9
+                       , InsertMove 2]
+               , OnSmash (OneOf [ Explode "healing mist"
+                                , Explode "healing mist"
+                                , Explode "pheromone"
+                                , Explode "distortion"
+                                , Explode "explosion blast 20" ]) ]
+  }
+potion10 = potion
+  { ifreq    = [("useful", 100), ("potion of glue", 1)]
+  , irarity  = [(1, 1)]
+  , icount   = 1 + d 2
+  , ieffects = [ NoEffect "sticky foam", Paralyze (5 + d 5)
+               , OnSmash (Explode "glue")]
+  }
+
+-- * Non-exploding consumables, not specifically designed for throwing
+
+constructionHooter = scroll
+  { iname    = "construction hooter"
+  , ifreq    = [("useful", 1), ("construction hooter", 1)]  -- extremely rare
+  , iflavour = zipPlain [BrRed]
+  , irarity  = [(1, 1)]
+  , iaspects = []
+  , ieffects = [Summon [("construction robot", 1)] $ 1 + dl 2]
+  , ifeature = ifeature scroll ++ [Identified]
+  , idesc    = "The single-use electronic overdrive hooter that construction robots use to warn about danger and call help in extreme emergency."
+  , ikit     = []
+  }
 scroll = ItemKind
   { isymbol  = '?'
   , iname    = "tablet"
-  , ifreq    = [("useful", 4)]
-  , iflavour = zipFancy darkCol  -- arcane and old
-  , icount   = intToDeep 1
-  , iverbApply   = "dial"
-  , iverbProject = "lob"
+  , ifreq    = [("useful", 100), ("any scroll", 100)]
+  , iflavour = zipFancy stdCol ++ zipPlain darkCol  -- arcane and old
+  , icount   = 1
+  , irarity  = [(1, 10), (10, 7)]
+  , iverbHit = "thump"
   , iweight  = 700
-  , itoThrow = -25  -- bad grip
-  , ifeature = []
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [ toVelocity 25  -- bad grip
+               , Applicable ]
+  , idesc    = "A standard issue spaceship crew tablet displaying a fixed infographic and a big button. Some of these still contain a one-time password authorizing a particular spaceship's infrastructure transition. It is unknown how the infrastructure might respond after so many years."
+  , ikit     = []
   }
 scroll1 = scroll
-  { ifreq    = [("useful", 2)]
-  , ifeature = [Cause $ CallFriend 1]
+  { irarity  = [(10, 2)]
+  , ieffects = [CallFriend 1]
   }
 scroll2 = scroll
-  { ifeature = [Cause $ Summon 1]
+  { irarity  = [(1, 5), (10, 3)]
+  , ieffects = [NoEffect "of fireworks", Explode "firecracker 7"]
   }
 scroll3 = scroll
-  { ifeature = [Cause $ Ascend 1]
+  { irarity  = [(1, 4), (10, 2)]
+  , ieffects = [Ascend 1]
   }
+scroll4 = scroll
+  { ieffects = [ OneOf [ Teleport $ 2 + d 5, RefillCalm 10, RefillCalm (-10)
+                       , InsertMove 4, Paralyze 10, Identify CGround ] ]
+  }
+scroll5 = scroll
+  { irarity  = [(1, 4), (10, 6)]
+  , ieffects = [ OneOf [ Summon standardSummon $ d 2
+                       , CallFriend 1, Ascend (-1), Ascend 1
+                       , RefillCalm 30, RefillCalm (-30), CreateItem $ d 2
+                       , PolyItem CGround ] ]
+               -- TODO: ask player: Escape 1
+  }
+scroll6 = scroll
+  { ieffects = [Teleport $ 2 + d 5]
+  }
+scroll7 = scroll
+  { irarity  = [(10, 2)]
+  , ieffects = [InsertMove $ d 2 + dl 2]
+  }
+scroll8 = scroll
+  { irarity  = [(3, 6), (10, 3)]
+  , ieffects = [Identify CGround]  -- TODO: ask player: AskPlayer cstore eff?
+  }
+scroll9 = scroll
+  { irarity  = [(3, 3), (10, 9)]
+  , ieffects = [PolyItem CGround]
+  }
+
+standardSummon :: Freqs
+standardSummon = [ ("alien", 20)
+                 , ("summonable animal", 50)
+                 , ("summonable robot", 30) ]
+
+-- * Armor
+
+armorLeather = ItemKind
+  { isymbol  = '['
+  , iname    = "spacesuit breastplate"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [Brown]
+  , icount   = 1
+  , irarity  = [(4, 9)]
+  , iverbHit = "thud"
+  , iweight  = 7000
+  , iaspects = [ AddHurtMelee (-3)
+               , AddArmorMelee $ (1 + dl 3) |*| 5
+               , AddArmorRanged $ (1 + dl 3) |*| 5 ]
+  , ieffects = []
+  , ifeature = [ toVelocity 30  -- unwieldy to throw and blunt
+               , Durable, EqpSlot EqpSlotAddArmorMelee "", Identified ]
+  , idesc    = "A hard-shell torso segment cut from a disposed off spacesuit."
+  , ikit     = []
+  }
+armorMail = armorLeather
+  { iname    = "bulletproof vest"
+  , iflavour = zipPlain [Cyan]
+  , irarity  = [(7, 9)]
+  , iweight  = 12000
+  , iaspects = [ AddHurtMelee (-3)
+               , AddArmorMelee $ (2 + dl 3) |*| 5
+               , AddArmorRanged $ (2 + dl 3) |*| 5 ]
+  , idesc    = "A civilian bulletproof vest. Discourages foes from attacking your torso, making it harder for them to land a blow."
+  }
+gloveFencing = ItemKind
+  { isymbol  = ']'
+  , iname    = "construction glove"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [BrYellow]
+  , icount   = 1
+  , irarity  = [(4, 6), (10, 12)]
+  , iverbHit = "flap"
+  , iweight  = 100
+  , iaspects = [ AddHurtMelee $ 2 * (d 3 + 2 * dl 5)
+               , AddArmorRanged $ d 2 + dl 2 ]
+  , ieffects = []
+  , ifeature = [ toVelocity 30  -- flaps and flutters
+               , Durable, EqpSlot EqpSlotAddArmorRanged "", Identified ]
+  , idesc    = "A flexible construction glove from rough leather ensuring a good grip. Also, quite effective in deflecting or even catching slow projectiles."
+  , ikit     = []
+  }
+gloveGauntlet = gloveFencing
+  { iname    = "spacesuit glove"
+  , irarity  = [(6, 12)]
+  , iflavour = zipPlain [BrCyan]
+  , iweight  = 300
+  , iaspects = [ AddArmorMelee $ 2 * (d 2 + dl 2)
+               , AddArmorRanged $ 2 * (d 2 + dl 2) ]
+  , idesc    = "A piece of a hull maintenance spacesuit, padded and reinforced with carbon fibre."
+  }
+gloveJousting = gloveFencing
+  { iname    = "welding handgear"
+  , irarity  = [(6, 6)]
+  , iflavour = zipFancy [BrRed]
+  , iweight  = 500
+  , iaspects = [ AddHurtMelee $ - 10 - d 5 + dl 5
+               , AddArmorMelee $ 2 * (d 2 + dl 3)
+               , AddArmorRanged $ 2 * (d 2 + dl 3) ]
+  , idesc    = "Rigid, bulky handgear embedding a welding equipment, complete with an affixed small shield and a darkened visor. Awe-inspiring."
+  }
+-- Shield doesn't protect against ranged attacks to prevent
+-- micromanagement: walking with shield, melee without.
+buckler = ItemKind
+  { isymbol  = ')'
+  , iname    = "buckler"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [Blue]
+  , icount   = 1
+  , irarity  = [(4, 7)]
+  , iverbHit = "bash"
+  , iweight  = 2000
+  , iaspects = [AddArmorMelee 40, AddHurtMelee (-30)]
+  , ieffects = []
+  , ifeature = [ toVelocity 30  -- unwieldy to throw and blunt
+               , Durable, EqpSlot EqpSlotAddArmorMelee "", Identified ]
+  , idesc    = "Heavy and unwieldy arm protection made from an outer airlock panel. Absorbs a percentage of melee damage, both dealt and sustained. Too small to intercept projectiles with."
+  , ikit     = []
+  }
+shield = buckler
+  { iname    = "shield"
+  , irarity  = [(7, 7)]
+  , iflavour = zipPlain [Green]
+  , iweight  = 3000
+  , iaspects = [AddArmorMelee 80, AddHurtMelee (-70)]
+  , ifeature = [ toVelocity 20  -- unwieldy to throw and blunt
+               , Durable, EqpSlot EqpSlotAddArmorMelee "", Identified ]
+  , idesc    = "Large and unwieldy rectangle made of anti-meteorite ceramic sheet. Absorbs a percentage of melee damage, both dealt and sustained. Too heavy to intercept projectiles with."
+  }
+
+-- * Weapons
+
+dagger = ItemKind
+  { isymbol  = '|'
+  , iname    = "kitchen knife"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [BrCyan]
+  , icount   = 1
+  , irarity  = [(1, 20), (10, 4)]
+  , iverbHit = "stab"
+  , iweight  = 1000
+  , iaspects = [AddHurtMelee $ 2 * (d 3 + 2 * dl 5), AddArmorMelee $ d 4 + dl 4]
+  , ieffects = [Hurt (4 * d 1)]
+  , ifeature = [ toVelocity 40  -- ensuring it hits with the tip costs speed
+               , Durable, EqpSlot EqpSlotWeapon "", Identified ]
+  , idesc    = "A heavy professional kitchen blade. Will do fine cutting any kind of meat and also thrusting and parrying blows. Does not penetrate deeply, but is hard to block. Especially useful in conjunction with a larger weapon."
+  , ikit     = []
+  }
+hammer = ItemKind
+  { isymbol  = '\\'
+  , iname    = "demolition hammer"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [BrMagenta]
+  , icount   = 1
+  , irarity  = [(4, 12), (10, 2)]
+  , iverbHit = "club"
+  , iweight  = 1500
+  , iaspects = [AddHurtMelee $ d 3 + 2 * dl 5]
+  , ieffects = [Hurt (6 * d 1)]
+  , ifeature = [ toVelocity 20  -- ensuring it hits with the sharp tip costs
+               , Durable, EqpSlot EqpSlotWeapon "", Identified ]
+  , idesc    = "A hammer on a long handle used for construction work. It may not cause grave wounds, but neither does it ricochet or glance off armor. Great sidearm for opportunistic blows against armored foes."
+  , ikit     = []
+  }
 sword = ItemKind
   { isymbol  = '/'
   , iname    = "sharpened pipe"
-  , ifreq    = [("useful", 40)]
-  , iflavour = zipPlain [Cyan]
-  , icount   = intToDeep 1
-  , iverbApply   = "hit"
-  , iverbProject = "heave"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [BrBlue]
+  , icount   = 1
+  , irarity  = [(3, 1), (6, 20), (10, 10)]
+  , iverbHit = "slash"
+  , iweight  = 2000
+  , iaspects = []
+  , ieffects = [Hurt (9 * d 1)]
+  , ifeature = [ toVelocity 20  -- ensuring it hits with the tip costs speed
+               , Durable, EqpSlot EqpSlotWeapon "", Identified ]
+  , idesc    = "A makeshift weapon of simple design, but great potential. Hard to master, though."
+  , ikit     = []
+  }
+halberd = ItemKind
+  { isymbol  = '/'
+  , iname    = "halberd"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [BrYellow]
+  , icount   = 1
+  , irarity  = [(7, 1), (10, 10)]
+  , iverbHit = "impale"
   , iweight  = 3000
-  , itoThrow = -25  -- bad grip
-  , ifeature = [Cause $ Hurt (rollDice 5 1) (rollDeep (1, 2) (4, 2))]
+  , iaspects = [AddArmorMelee $ 2 * (d 4 + dl 4)]
+  , ieffects = [Hurt (12 * d 1)]
+  , ifeature = [ toVelocity 20  -- not balanced
+               , Durable, EqpSlot EqpSlotWeapon "", Identified ]
+  , idesc    = "A perfect replica made for a reenactor troupe, missing only some sharpening. Versatile, with great reach and leverage. Foes are held at a distance."
+  , ikit     = []
   }
+
+-- * Wands
+
 wand = ItemKind
   { isymbol  = '-'
   , iname    = "injector"
-  , ifreq    = [("useful", 5)]
+  , ifreq    = [("useful", 100)]
   , iflavour = zipFancy brightCol
-  , icount   = intToDeep 1
-  , iverbApply   = "snap"
-  , iverbProject = "zap"
+  , icount   = 1
+  , irarity  = []  -- TODO: add charges, etc.
+  , iverbHit = "club"
   , iweight  = 300
-  , itoThrow = 25  -- jet
-  , ifeature = [Fragile]
+  , iaspects = [AddLight 1, AddSpeed (-1)]  -- pulsing with power, distracts
+  , ieffects = []
+  , ifeature = [ toVelocity 125  -- sufficiently advanced tech
+               , Applicable, Durable ]
+  , idesc    = "Buzzing with dazzling light that shines even through appendages that handle it."
+  , ikit     = []
   }
 wand1 = wand
-  { ifeature = ifeature wand ++ [Cause Dominate]
+  { ieffects = []  -- TODO: emit a cone of sound shrapnel that makes enemy cover his ears and so drop '{'
   }
 wand2 = wand
-  { ifreq    = [("useful", 2)]
-  , ifeature = ifeature wand ++ [Cause $ Heal (-25)]
-  }
-fist = sword
-  { isymbol  = '@'
-  , iname    = "fist"
-  , ifreq    = [("hth", 1), ("unarmed", 100)]
-  , iverbApply   = "punch"
-  , iverbProject = "ERROR, please report: iverbProject fist"
-  , ifeature = [Cause $ Hurt (rollDice 5 1) (intToDeep 0)]
-  }
-foot = sword
-  { isymbol  = '@'
-  , iname    = "foot"
-  , ifreq    = [("hth", 1), ("unarmed", 50)]
-  , iverbApply   = "kick"
-  , iverbProject = "ERROR, please report: iverbProject foot"
-  , ifeature = [Cause $ Hurt (rollDice 5 1) (intToDeep 0)]
-  }
-tentacle = sword
-  { isymbol  = 'S'
-  , iname    = "tentacle"
-  , ifreq    = [("hth", 1), ("monstrous", 100)]
-  , iverbApply   = "hit"
-  , iverbProject = "ERROR, please report: iverbProject tentacle"
-  , ifeature = [Cause $ Hurt (rollDice 5 1) (intToDeep 0)]
-  }
-fragrance = ItemKind
-  { isymbol  = '\''
-  , iname    = "fragrance"
-  , ifreq    = [("fragrance", 1)]
-  , iflavour = zipFancy [BrMagenta]
-  , icount   = rollDeep (5, 2) (0, 0)
-  , iverbApply   = "smell"
-  , iverbProject = "exude"
-  , iweight  = 1
-  , itoThrow = -93  -- the slowest that gets anywhere (1 step only)
-  , ifeature = [Fragile]
-  }
-mist_healing = ItemKind
-  { isymbol  = '\''
-  , iname    = "mist"
-  , ifreq    = [("mist healing", 1)]
-  , iflavour = zipFancy [White]
-  , icount   = rollDeep (12, 2) (0, 0)
-  , iverbApply   = "inhale"
-  , iverbProject = "blow"
-  , iweight  = 1
-  , itoThrow = -87  -- the slowest that travels at least 2 steps
-  , ifeature = [Cause $ Heal 1, Fragile]
-  }
-mist_wounding = ItemKind
-  { isymbol  = '\''
-  , iname    = "mist"
-  , ifreq    = [("mist wounding", 1)]
-  , iflavour = zipFancy [White]
-  , icount   = rollDeep (12, 2) (0, 0)
-  , iverbApply   = "inhale"
-  , iverbProject = "blow"
-  , iweight  = 1
-  , itoThrow = -87
-  , ifeature = [Cause $ Heal (-1), Fragile]
+  { ieffects = []
   }
-glass_piece = ItemKind
-  { isymbol  = '\''
-  , iname    = "glass piece"
-  , ifreq    = [("glass piece", 1)]
-  , iflavour = zipPlain [BrBlue]
-  , icount   = rollDeep (10, 2) (0, 0)
-  , iverbApply   = "grate"
-  , iverbProject = "toss"
-  , iweight  = 10
-  , itoThrow = 0
-  , ifeature = [Cause $ Hurt (rollDice 1 1) (intToDeep 0), Fragile, Linger 20]
+
+-- * Assorted tools
+
+jumpingPole = ItemKind
+  { isymbol  = '~'
+  , iname    = "jumping pole"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [White]
+  , icount   = 1
+  , irarity  = [(1, 4), (10, 2)]
+  , iverbHit = "prod"
+  , iweight  = 10000
+  , iaspects = []
+  , ieffects = [InsertMove 2]  -- TODO: implement with timed speed instead
+                               -- and then make Durable, freq 2, and just trade
+                               -- taken turn now for a free turn later
+  , ifeature = [Applicable, Identified]
+  , idesc    = "Makes you vulnerable at take-off, but then you are free like a bird."
+  , ikit     = []
   }
-smoke = ItemKind
-  { isymbol  = '\''
-  , iname    = "smoke"
-  , ifreq    = [("smoke", 1)]
-  , iflavour = zipPlain [BrBlack]
-  , icount   = rollDeep (12, 2) (0, 0)
-  , iverbApply   = "inhale"
-  , iverbProject = "blow"
-  , iweight  = 1
-  , itoThrow = -70
-  , ifeature = [Fragile]
+honingSteel = ItemKind
+  { isymbol  = '~'
+  , iname    = "honing steel"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [Blue]
+  , icount   = 1
+  , irarity  = [(5, 5)]
+  , iverbHit = "prod"
+  , iweight  = 400
+  , iaspects = [AddHurtMelee $ 2 * (d 3 + 2 * dl 5)]
+  , ieffects = []
+  , ifeature = [EqpSlot EqpSlotAddHurtMelee "", Identified]
+  , idesc    = "Originally used for realigning the bent or buckled edges of kitchen knives in the local bars. Now it saves lives by letting you fix your weapons between or even during fights, without the need to set up camp, fish out tools and assemble a proper sharpening workshop."
+  , ikit     = []
   }
diff --git a/GameDefinition/Content/ItemKindActor.hs b/GameDefinition/Content/ItemKindActor.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/ItemKindActor.hs
@@ -0,0 +1,477 @@
+-- Copyright (c) 2008--2011 Andres Loeh, 2010--2014 Mikolaj Konarski
+-- This file is a part of the computer game Allure of the Stars
+-- and is released under the terms of the GNU Affero General Public License.
+-- For license and copyright information, see the file LICENSE.
+--
+-- | Actor (or rather actor body trunk) definitions.
+module Content.ItemKindActor ( actors ) where
+
+import qualified Data.EnumMap.Strict as EM
+
+import Game.LambdaHack.Common.Ability
+import Game.LambdaHack.Common.Color
+import Game.LambdaHack.Common.Effect
+import Game.LambdaHack.Common.Flavour
+import Game.LambdaHack.Common.Misc
+import Game.LambdaHack.Content.ItemKind
+
+actors :: [ItemKind]
+actors =
+  [warrior, pilot, engineer, doctor, clerk, hairdresser, lawyer, peddler, taxCollector, eye, fastEye, nose, elbow, armadillo, gilaMonster, komodoDragon, hyena, alligator, hornetSwarm, thornbush, razorwireFence, electricFence, activeFence, steamFaucet, biogasFaucet, shepherdDrone, huntingDrone, homeRobot, wasteRobot, lightRobot, heavyRobot]
+
+warrior,    pilot, engineer, doctor, clerk, hairdresser, lawyer, peddler, taxCollector, eye, fastEye, nose, elbow, armadillo, gilaMonster, komodoDragon, hyena, alligator, hornetSwarm, thornbush, razorwireFence, electricFence, activeFence, steamFaucet, biogasFaucet, shepherdDrone, huntingDrone, homeRobot, wasteRobot, lightRobot, heavyRobot :: ItemKind
+
+-- * Hunams
+
+warrior = ItemKind
+  { isymbol  = '@'
+  , iname    = "mercenary"  -- modified if in hero faction
+  , ifreq    = [("hero", 100), ("civilian", 100)]
+  , iflavour = zipPlain [BrBlack]  -- modified if in hero faction
+  , icount   = 1
+  , irarity  = [(1, 5)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 50, AddMaxCalm 60, AddSpeed 20
+               , AddSight 3 ]  -- no via eyes, but feel, hearing, etc.
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  , ikit     = [("fist", COrgan), ("foot", COrgan), ("eye 4", COrgan)]
+  }
+pilot = warrior
+  { iname    = "pilot" }
+engineer = warrior
+  { iname    = "engineer" }
+doctor = warrior
+  { iname    = "doctor" }
+
+clerk = warrior
+  { iname    = "clerk"
+  , ifreq    = [("civilian", 100)] }
+hairdresser = clerk
+  { iname    = "hairdresser" }
+lawyer = clerk
+  { iname    = "lawyer" }
+peddler = clerk
+  { iname    = "peddler" }
+taxCollector = clerk
+  { iname    = "tax collector" }
+
+-- * Aliens
+
+eye = ItemKind
+  { isymbol  = 'w'
+  , iname    = "beckoning walker"
+  , ifreq    = [("alien", 100), ("horror", 100)]
+  , iflavour = zipPlain [BrRed]
+  , icount   = 1
+  , irarity  = [(3, 1), (10, 9)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 20, AddMaxCalm 60, AddSpeed 20
+               , AddSight 4 ]  -- can shoot for as long as lives
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Walks with a stately dignity. You read death in the slow beckoning gestures of its revolting upper appendages."
+  , ikit     = [ ("foot", COrgan)
+               , ("tentacle", COrgan), ("tentacle", COrgan)
+               , ("tentacle", COrgan)
+               , ("eye 4", COrgan) ]
+  }
+fastEye = ItemKind
+  { isymbol  = 'b'
+  , iname    = "crawling biter"
+  , ifreq    = [("alien", 100), ("horror", 100)]
+  , iflavour = zipPlain [BrBlue]
+  , icount   = 1
+  , irarity  = [(6, 1), (10, 7)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 6, AddMaxCalm 60, AddSpeed 30
+               , AddSight 4 ]  -- can shoot for as long as lives
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "It bites as blindingly fast as it runs. Or rolls? Or crawls? Also, cuts and pierces."
+  , ikit     = [ ("tentacle", COrgan), ("tentacle", COrgan)
+               , ("tentacle", COrgan), ("tentacle", COrgan)
+               , ("jaw", COrgan)
+               , ("eye 4", COrgan), ("speed gland 10", COrgan) ]
+  }
+nose = ItemKind
+  { isymbol  = 'h'
+  , iname    = "tentacled horror"
+  , ifreq    = [("alien", 100), ("horror", 100)]
+  , iflavour = zipPlain [BrGreen]
+  , icount   = 1
+  , irarity  = [(3, 1), (10, 9)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 40, AddMaxCalm 30, AddSpeed 18
+               , AddSmell 3 ]  -- depends solely on smell
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "A blind, slimy mass of clawing, stinging and burning. You'd think it's mindless, but as soon as it touches your trembling body, it's always one step ahead."
+  , ikit     = [ ("smallClaw", COrgan)
+               , ("tentacle", COrgan), ("tentacle", COrgan)
+               , ("tentacle", COrgan), ("tentacle", COrgan)
+               , ("thorn", COrgan), ("sting", COrgan) ]
+  }
+elbow = ItemKind
+  { isymbol  = 's'
+  , iname    = "creepy shooter"
+  , ifreq    = [("alien", 100), ("horror", 100)]
+  , iflavour = zipPlain [BrMagenta]
+  , icount   = 1
+  , irarity  = [(6, 1), (10, 5)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 15, AddMaxCalm 80, AddSpeed 26
+               , AddSkills $ EM.singleton AbMelee (-1)
+               , AddSight 15 ]  -- can shoot for as long as lives
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "It moves in sudden jerks and never makes a noise. Speaks in hard objects hurled at deadly speeds."
+  , ikit     = [ ("speed gland 4", COrgan)
+               , ("any arrow", CInv), ("any arrow", CInv)
+               , ("any arrow", CInv), ("any arrow", CInv) ]
+  }
+
+-- * Animals
+
+armadillo = ItemKind
+  { isymbol  = 'a'
+  , iname    = "giant armadillo"
+  , ifreq    = [("animal", 100), ("horror", 100), ("summonable animal", 100)]
+  , iflavour = zipPlain [Brown]
+  , icount   = 1
+  , irarity  = [(1, 5)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 30, AddMaxCalm 30, AddSpeed 18
+               , AddSkills $ EM.singleton AbAlter (-1)
+               , AddSight 3 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  , ikit     = [ ("claw", COrgan), ("snout", COrgan), ("armored skin", COrgan)
+               , ("nostril", COrgan) ]
+  }
+gilaMonster = ItemKind
+  { isymbol  = 'g'
+  , iname    = "Gila monster"
+  , ifreq    = [("animal", 100), ("horror", 100), ("summonable animal", 100)]
+  , iflavour = zipPlain [Magenta]
+  , icount   = 1
+  , irarity  = [(2, 5), (10, 3)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 15, AddMaxCalm 60, AddSpeed 15
+               , AddSkills $ EM.singleton AbAlter (-1)
+               , AddSight 3 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  , ikit     = [ ("venom tooth", COrgan), ("small claw", COrgan)
+               , ("eye 4", COrgan), ("nostril", COrgan) ]
+  }
+komodoDragon = ItemKind  -- bad hearing
+  { isymbol  = 'k'
+  , iname    = "Komodo dragon"
+  , ifreq    = [("animal", 100), ("horror", 100), ("summonable animal", 100)]
+  , iflavour = zipPlain [Blue]
+  , icount   = 1
+  , irarity  = [(5, 5), (10, 7)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 40, AddMaxCalm 60, AddSpeed 18
+               , AddSight 3 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  , ikit     = [ ("large tail", COrgan), ("jaw", COrgan), ("small claw", COrgan)
+               , ("speed gland 4", COrgan), ("armored skin", COrgan)
+               , ("eye 2", COrgan), ("nostril", COrgan) ]
+  }
+hyena = ItemKind
+  { isymbol  = 'h'
+  , iname    = "spotted hyena"
+  , ifreq    = [("animal", 100), ("horror", 100), ("summonable animal", 100)]
+  , iflavour = zipPlain [Red]
+  , icount   = 1
+  , irarity  = [(4, 6), (10, 6)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 30, AddMaxCalm 60, AddSpeed 35
+               , AddSight 3 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  , ikit     = [("jaw", COrgan), ("eye 4", COrgan), ("nostril", COrgan)]
+  }
+alligator = ItemKind
+  { isymbol  = 'a'
+  , iname    = "alligator"
+  , ifreq    = [("animal", 100), ("horror", 100), ("summonable animal", 100)]
+  , iflavour = zipPlain [Blue]
+  , icount   = 1
+  , irarity  = [(10, 8)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 30, AddMaxCalm 60, AddSpeed 17
+               , AddArmorMelee 30, AddArmorRanged 30
+               , AddSight 3 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  , ikit     = [ ("large jaw", COrgan), ("large tail", COrgan), ("claw", COrgan)
+               , ("armored skin", COrgan), ("eye 4", COrgan) ]
+  }
+
+-- * Non-animal animals
+
+hornetSwarm = ItemKind
+  { isymbol  = 'h'
+  , iname    = "hornet swarm"
+  , ifreq    = [("animal", 100), ("horror", 100), ("summonable animal", 100)]
+  , iflavour = zipPlain [Magenta]
+  , icount   = 1
+  , irarity  = [(5, 1), (10, 5)]
+  , iverbHit = "thud"
+  , iweight  = 1000
+  , iaspects = [ AddMaxHP 5, AddMaxCalm 60, AddSpeed 30, AddSight 2
+               , AddSkills $ EM.singleton AbAlter (-1)
+               , AddArmorMelee 90, AddArmorRanged 90 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  , ikit     = [("sting", COrgan)]
+  }
+thornbush = ItemKind
+  { isymbol  = 'b'
+  , iname    = "rose bush"
+  , ifreq    = [("animal", 100)]
+  , iflavour = zipPlain [Brown]
+  , icount   = 1
+  , irarity  = [(1, 1)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 20, AddMaxCalm 999, AddSpeed 20
+               , AddSkills
+                 $ EM.fromDistinctAscList (zip [minBound..maxBound] [-1, -1..])
+                   `addSkills` EM.fromList (zip [AbWait, AbMelee] [1, 1..])
+               , AddArmorMelee 30, AddArmorRanged 30 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  , ikit     = [("thorn", COrgan)]
+  }
+
+-- * Robots
+
+razorwireFence = ItemKind
+  { isymbol  = 'f'
+  , iname    = "razorwire fence"
+  , ifreq    = [("robot", 100)]
+  , iflavour = zipPlain [Cyan]
+  , icount   = 1
+  , irarity  = [(1, 1)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 30, AddMaxCalm 999, AddSpeed 20
+               , AddSkills
+                 $ EM.fromDistinctAscList (zip [minBound..maxBound] [-1, -1..])
+                   `addSkills` EM.fromList (zip [AbWait, AbMelee] [1, 1..])
+               , AddArmorMelee 50, AddArmorRanged 50 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Must have been bought by previous ship owners to contain the wild animal infestation."
+  , ikit     = [("razor", COrgan)]
+  }
+electricFence = ItemKind
+  { isymbol  = 'f'
+  , iname    = "electric fence"
+  , ifreq    = [("robot", 100)]
+  , iflavour = zipPlain [Blue]
+  , icount   = 1
+  , irarity  = [(1, 1)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 10, AddMaxCalm 999, AddSpeed 50
+               , AddSkills
+                 $ EM.fromDistinctAscList (zip [minBound..maxBound] [-1, -1..])
+                   `addSkills` EM.fromList (zip [AbWait, AbMelee] [1, 1..])
+               , AddArmorMelee 50, AddArmorRanged 50 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Marginally intelligent electric shepherd. Originally used in the spaceship's dairy farm and the zoo level."
+  , ikit     = [("live wire", COrgan)]
+  }
+activeFence = ItemKind
+  { isymbol  = 'f'
+  , iname    = "active fence"
+  , ifreq    = [("robot", 100)]
+  , iflavour = zipPlain [Red]
+  , icount   = 1
+  , irarity  = [(4, 0), (5, 1)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 30, AddMaxCalm 999, AddSpeed 20, AddSight 6
+               , AddSkills
+                 $ EM.fromDistinctAscList (zip [minBound..maxBound] [-1, -1..])
+                   `addSkills` EM.fromList (zip [AbWait, AbProject]
+                                                [1, 1..])
+               , AddArmorMelee 50, AddArmorRanged 50 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Makeshift, mostly non-lethal, autonomous perimeter defense outpost."
+  , ikit     = [ ("needle", CInv), ("potion of glue", CInv)
+               , ("potion of glue", CInv), ("potion of glue", CInv) ]
+  }
+steamFaucet = ItemKind
+  { isymbol  = 'f'
+  , iname    = "steam faucet"
+  , ifreq    = [("robot", 100)]
+  , iflavour = zipPlain [BrWhite]
+  , icount   = 1
+  , irarity  = [(1, 1)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 100, AddMaxCalm 999, AddSpeed 5
+               , AddSkills
+                 $ EM.fromDistinctAscList (zip [minBound..maxBound] [-1, -1..])
+                   `addSkills` EM.fromList (zip [AbWait, AbMelee] [1, 1..]) ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "A cracked valve on one of the superheated water pipes spreading radially outward from the tokamak level."
+  , ikit     = [("boiling vent", COrgan), ("fissure", COrgan)]
+  }
+biogasFaucet = ItemKind
+  { isymbol  = 'f'
+  , iname    = "biogas faucet"
+  , ifreq    = [("robot", 33)]  -- very rare
+  , iflavour = zipPlain [BrGreen]
+  , icount   = 1
+  , irarity  = [(3, 0), (4, 1)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 100, AddMaxCalm 999, AddSpeed 5
+               , AddSkills
+                 $ EM.fromDistinctAscList (zip [minBound..maxBound] [-1, -1..])
+                   `addSkills` EM.fromList (zip [AbWait] [1, 1..]) ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "An emergency pressure-release vent on a liquefied biogas pipe."
+  , ikit     = [("explosion vent", COrgan)]
+  }
+shepherdDrone = ItemKind
+  { isymbol  = 'd'
+  , iname    = "shepherd drone"
+  , ifreq    = [("robot", 100), ("horror", 100), ("summonable robot", 100)]
+  , iflavour = zipPlain [BrRed]
+  , icount   = 1
+  , irarity  = [(1, 2)]
+  , iverbHit = "thud"
+  , iweight  = 1000
+  , iaspects = [ AddMaxHP 2, AddMaxCalm 60, AddSpeed 30, AddSight 4
+               , AddSkills
+                 $ EM.fromDistinctAscList
+                 $ zip [AbDisplace, AbMoveItem, AbProject] [-1, -1..]
+               , AddArmorMelee 90, AddArmorRanged 90 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "A shabby drone for bringing cows home."
+  , ikit     = [("eye2", COrgan), ("live wire", COrgan)]
+  }
+huntingDrone = ItemKind
+  { isymbol  = 'd'
+  , iname    = "hunting drone"
+  , ifreq    = [("robot", 100), ("horror", 100), ("summonable robot", 100)]
+  , iflavour = zipPlain [Green]
+  , icount   = 1
+  , irarity  = [(4, 0), (5, 1), (10, 2)]
+  , iverbHit = "thud"
+  , iweight  = 500
+  , iaspects = [ AddMaxHP 2, AddMaxCalm 60, AddSpeed 40, AddSight 4
+               , AddSkills
+                 $ EM.fromDistinctAscList
+                 $ zip [AbDisplace, AbMoveItem, AbMelee] [-1, -1..]
+               , AddArmorMelee 90, AddArmorRanged 90 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Originally designed for hunting down and putting to sleep stray animals. The sleeping agent has long since dried up."
+  , ikit     = [("eye2", COrgan), ("needle", CInv)]
+  }
+homeRobot = ItemKind
+  { isymbol  = 'r'
+  , iname    = "feral home robot"
+               -- TODO: name another 'deranged', tertiary imperative: survival
+  , ifreq    = [("robot", 100), ("horror", 100), ("summonable robot", 100)]
+  , iflavour = zipPlain [Magenta]
+  , icount   = 1
+  , irarity  = [(1, 10), (10, 6)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 10, AddMaxCalm 60, AddSpeed 20
+               , AddSmell 2 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Once a timid household robot, it magnificently adapted to the deadly environment."
+  , ikit     = [("fist", COrgan), ("eye2", COrgan)]
+  }
+wasteRobot = ItemKind
+  { isymbol  = 'r'
+  , iname    = "waste disposal robot"
+  , ifreq    = [ ("robot", 100), ("horror", 100), ("summonable robot", 100)
+               , ("construction robot", 1) ]
+  , iflavour = zipPlain [Green]
+  , icount   = 1
+  , irarity  = [(1, 10), (10, 6)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 20, AddMaxCalm 60, AddSpeed 15
+               , AddSmell 2 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "You are not in its database, hence you are waste."
+  , ikit     = [ ("jaw", COrgan), ("tentacle", COrgan)
+               , ("waste container", COrgan)
+               , ("armored skin", COrgan), ("eye3", COrgan) ]
+  }
+lightRobot = ItemKind
+  { isymbol  = 'r'
+  , iname    = "decoration robot"
+  , ifreq    = [ ("robot", 100), ("horror", 100), ("summonable robot", 100)
+               , ("construction robot", 1) ]
+  , iflavour = zipPlain [BrYellow]
+  , icount   = 1
+  , irarity  = [(1, 8), (10, 8)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 20, AddMaxCalm 60, AddSpeed 30
+               , AddSight 3, AddLight 3 ]  -- light can't be turned off
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Interior and exterior decoration robot. Strongly fancies deep reds recently."
+  , ikit     = [ ("claw", COrgan), ("tentacle", COrgan)
+               , ("armored skin", COrgan), ("eye4", COrgan) ]
+  }
+heavyRobot = ItemKind
+  { isymbol  = 'r'
+  , iname    = "construction robot"
+  , ifreq    = [ ("robot", 100), ("horror", 100), ("summonable robot", 100)
+               , ("construction robot", 100) ]
+  , iflavour = zipPlain [BrRed]
+  , icount   = 1
+  , irarity  = [(1, 4), (10, 10)]
+  , iverbHit = "thud"
+  , iweight  = 800000
+  , iaspects = [ AddMaxHP 80, AddMaxCalm 60, AddSpeed 20
+               , AddSight 3, AddLight 4 ]  -- light can't be turned off
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Heavy multi-purpose construction robot. Excels at discharging, dismantling and demolition."
+  , ikit     = [ ("largeJaw", COrgan), ("claw", COrgan)
+               , ("construction hooter", CInv)
+               , ("armored skin", COrgan), ("eye3", COrgan) ]
+  }
diff --git a/GameDefinition/Content/ItemKindOrgan.hs b/GameDefinition/Content/ItemKindOrgan.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/ItemKindOrgan.hs
@@ -0,0 +1,258 @@
+-- Copyright (c) 2008--2011 Andres Loeh, 2010--2014 Mikolaj Konarski
+-- This file is a part of the computer game Allure of the Stars
+-- and is released under the terms of the GNU Affero General Public License.
+-- For license and copyright information, see the file LICENSE.
+--
+-- | Organ definitions.
+module Content.ItemKindOrgan ( organs ) where
+
+import Game.LambdaHack.Common.Color
+import Game.LambdaHack.Common.Dice
+import Game.LambdaHack.Common.Effect
+import Game.LambdaHack.Common.Flavour
+import Game.LambdaHack.Common.Msg
+import Game.LambdaHack.Content.ItemKind
+
+organs :: [ItemKind]
+organs =
+  [fist, foot, tentacle, claw, smallClaw, snout, sting, venomTooth, venomFang, largeTail, jaw, largeJaw, armoredSkin, speedGland2, speedGland4, speedGland6, speedGland8, speedGland10, eye2, eye3, eye4, eye5, nostril, thorn, razor, liveWire, boilingVent, explosionVent, fissure, wasteContainer]
+
+fist,    foot, tentacle, claw, smallClaw, snout, sting, venomTooth, venomFang, largeTail, jaw, largeJaw, armoredSkin, speedGland2, speedGland4, speedGland6, speedGland8, speedGland10, eye2, eye3, eye4, eye5, nostril, thorn, razor, liveWire, boilingVent, explosionVent, fissure, wasteContainer :: ItemKind
+
+-- * Parameterized organs
+
+speedGland :: Int -> ItemKind
+speedGland n = fist
+  { iname    = "speed gland"
+  , ifreq    = [("speed gland" <+> tshow n, 100)]
+  , icount   = 1
+  , iverbHit = "spit at"
+  , iaspects = [AddSpeed $ intToDice n, Periodic $ intToDice n]
+  , ieffects = [RefillHP 1]
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
+speedGland2 = speedGland 2
+speedGland4 = speedGland 4
+speedGland6 = speedGland 6
+speedGland8 = speedGland 8
+speedGland10 = speedGland 10
+eye :: Int -> ItemKind
+eye n = fist
+  { iname    = "eye"
+  , ifreq    = [("eye" <+> tshow n, 100)]
+  , icount   = 2
+  , iverbHit = "glare at"
+  , iaspects = [AddSight (intToDice n)]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
+eye2 = eye 2
+eye3 = eye 3
+eye4 = eye 4
+eye5 = eye 5
+
+-- * Human weapon organs
+
+fist = ItemKind
+  { isymbol  = '%'
+  , iname    = "fist"
+  , ifreq    = [("fist", 100)]
+  , iflavour = zipPlain [BrRed]
+  , icount   = 2
+  , irarity  = [(1, 1)]
+  , iverbHit = "punch"
+  , iweight  = 2000
+  , iaspects = []
+  , ieffects = [Hurt (4 * d 1)]
+  , ifeature = [Durable, EqpSlot EqpSlotWeapon "", Identified]
+  , idesc    = ""
+  , ikit     = []
+  }
+foot = fist
+  { iname    = "foot"
+  , ifreq    = [("foot", 50)]
+  , icount   = 2
+  , iverbHit = "kick"
+  , ieffects = [Hurt (4 * d 1)]
+  , idesc    = ""
+  }
+
+-- * Universal weapon organs
+
+claw = fist
+  { iname    = "claw"
+  , ifreq    = [("claw", 50)]
+  , icount   = 2  -- even if more, only the fore claws used for fighting
+  , iverbHit = "slash"
+  , ieffects = [Hurt (6 * d 1)]
+  , idesc    = ""
+  }
+smallClaw = fist
+  { iname    = "small claw"
+  , ifreq    = [("small claw", 50)]
+  , icount   = 2
+  , iverbHit = "slash"
+  , ieffects = [Hurt (3 * d 1)]
+  , idesc    = ""
+  }
+snout = fist
+  { iname    = "snout"
+  , ifreq    = [("snout", 10)]
+  , iverbHit = "bite"
+  , ieffects = [Hurt (2 * d 1)]
+  , idesc    = ""
+  }
+jaw = fist
+  { iname    = "jaw"
+  , ifreq    = [("jaw", 20)]
+  , icount   = 1
+  , iverbHit = "rip"
+  , ieffects = [Hurt (5 * d 1)]
+  , idesc    = ""
+  }
+largeJaw = fist
+  { iname    = "large jaw"
+  , ifreq    = [("large jaw", 100)]
+  , icount   = 1
+  , iverbHit = "crush"
+  , ieffects = [Hurt (12 * d 1)]
+  , idesc    = ""
+  }
+tentacle = fist
+  { iname    = "tentacle"
+  , ifreq    = [("tentacle", 50)]
+  , icount   = 4
+  , iverbHit = "slap"
+  , ieffects = [Hurt (4 * d 1)]
+  , idesc    = ""
+  }
+
+-- * Special weapon organs
+
+thorn = fist
+  { iname    = "thorn"
+  , ifreq    = [("thorn", 100)]
+  , icount   = 7
+  , iverbHit = "impale"
+  , ieffects = [Hurt (1 * d 1)]
+  , idesc    = ""
+  }
+razor = fist
+  { iname    = "razor"
+  , ifreq    = [("razor", 100)]
+  , icount   = 7
+  , iverbHit = "slice"
+  , ieffects = [Hurt (2 * d 1)]
+  , idesc    = ""
+  }
+fissure = fist
+  { iname    = "fissure"
+  , ifreq    = [("fissure", 100)]
+  , icount   = 2
+  , iverbHit = "hiss at"
+  , ieffects = [Burn 1]
+  , idesc    = ""
+  }
+sting = fist
+  { iname    = "sting"
+  , ifreq    = [("sting", 100)]
+  , icount   = 1
+  , iverbHit = "sting"
+  , ieffects = [Burn 1, Paralyze 2]
+  , idesc    = ""
+  }
+venomTooth = fist
+  { iname    = "venom tooth"
+  , ifreq    = [("venom tooth", 100)]
+  , icount   = 2
+  , iverbHit = "bite"
+  , ieffects = [Hurt (3 * d 1), Paralyze 3]
+  , idesc    = ""
+  }
+venomFang = fist
+  { iname    = "venom fang"
+  , ifreq    = [("venom fang", 100)]
+  , icount   = 2
+  , iverbHit = "bite"
+  , ieffects = [Hurt (3 * d 1)]  -- TODO: +12 damage or poison effect
+  , idesc    = ""
+  }
+largeTail = fist
+  { iname    = "large tail"
+  , ifreq    = [("large tail", 50)]
+  , icount   = 1
+  , iverbHit = "knock"
+  , ieffects = [Hurt (8 * d 1), PushActor (ThrowMod 400 25)]
+  , idesc    = ""
+  }
+liveWire = fist
+  { iname    = "live wire"
+  , ifreq    = [("live wire", 100)]
+  , icount   = 4
+  , iverbHit = "shock"
+  , iaspects = []
+  , ieffects = [Hurt (1 * d 1), Paralyze 1]
+  , idesc    = ""
+  }
+
+-- * Armor organs
+
+armoredSkin = fist
+  { iname    = "armored skin"
+  , ifreq    = [("armored skin", 100)]
+  , icount   = 1
+  , iverbHit = "bash"
+  , iaspects = [AddArmorMelee 33, AddArmorRanged 33]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
+
+-- * Sense organs
+
+nostril = fist
+  { iname    = "nostril"
+  , ifreq    = [("nostril", 100)]
+  , icount   = 2
+  , iverbHit = "snuff"
+  , iaspects = [AddSmell 1]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
+
+-- * Assorted
+
+boilingVent = fist
+  { iname    = "vent"
+  , ifreq    = [("boiling vent", 100)]
+  , icount   = 1
+  , iverbHit = "menace"
+  , iaspects = [Periodic $ 1 + d 2]
+  , ieffects = [Explode "boiling water"]
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
+explosionVent = fist
+  { iname    = "vent"
+  , ifreq    = [("explosion vent", 100)]
+  , icount   = 1
+  , iverbHit = "menace"
+  , iaspects = [Periodic $ 2 + d 2]
+  , ieffects = [Explode "explosion blast 20"]
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
+wasteContainer = fist
+  { iname    = "waste container"
+  , ifreq    = [("waste container", 100)]
+  , icount   = 1
+  , iverbHit = "spill over"
+  , iaspects = [Periodic 1]
+  , ieffects = [ Summon [("summonable animal", 1)] $ 1 + dl 2, RefillHP 1
+               , Explode "waste" ]
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
diff --git a/GameDefinition/Content/ItemKindShrapnel.hs b/GameDefinition/Content/ItemKindShrapnel.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/ItemKindShrapnel.hs
@@ -0,0 +1,244 @@
+-- Copyright (c) 2008--2011 Andres Loeh, 2010--2014 Mikolaj Konarski
+-- This file is a part of the computer game Allure of the Stars
+-- and is released under the terms of the GNU Affero General Public License.
+-- For license and copyright information, see the file LICENSE.
+--
+-- | Shrapnel definitions.
+module Content.ItemKindShrapnel ( shrapnels ) where
+
+import Game.LambdaHack.Common.Color
+import Game.LambdaHack.Common.Dice
+import Game.LambdaHack.Common.Effect
+import Game.LambdaHack.Common.Flavour
+import Game.LambdaHack.Common.Msg
+import Game.LambdaHack.Content.ItemKind
+
+shrapnels :: [ItemKind]
+shrapnels =
+  [fragrance, pheromone, firecracker2, firecracker3, firecracker4, firecracker5, firecracker6, firecracker7, mistHealing, mistWounding, distortion, waste, burningOil2, burningOil3, burningOil4, explosionBlast10, explosionBlast20, glassPiece, smoke, boilingWater, glue]
+
+fragrance,    pheromone, firecracker2, firecracker3, firecracker4, firecracker5, firecracker6, firecracker7, mistHealing, mistWounding, distortion, waste, burningOil2, burningOil3, burningOil4, explosionBlast10, explosionBlast20, glassPiece, smoke, boilingWater, glue :: ItemKind
+
+-- * Parameterized shrapnel
+
+burningOil :: Int -> ItemKind
+burningOil n = ItemKind
+  { isymbol  = '*'
+  , iname    = "burning oil"
+  , ifreq    = [("burning oil" <+> tshow n, 1)]
+  , iflavour = zipFancy [BrYellow]
+  , icount   = intToDice (n * 4)
+  , irarity  = [(1, 1)]
+  , iverbHit = "burn"
+  , iweight  = 1
+  , iaspects = [AddLight 2]
+  , ieffects = [ Burn (n `div` 2)
+               , Paralyze (intToDice $ n `div` 2) ]  -- tripping on oil
+  , ifeature = [ toVelocity (min 100 $ n * 7)
+               , Fragile, Identified ]
+  , idesc    = "Sticky oil, burning brightly."
+  , ikit     = []
+  }
+burningOil2 = burningOil 2
+burningOil3 = burningOil 3
+burningOil4 = burningOil 4
+explosionBlast :: Int -> ItemKind
+explosionBlast n = ItemKind
+  { isymbol  = '*'
+  , iname    = "explosion blast"
+  , ifreq    = [("explosion blast" <+> tshow n, 1)]
+  , iflavour = zipPlain [BrRed]
+  , icount   = 12  -- strong, but few, so not always hits target
+  , irarity  = [(1, 1)]
+  , iverbHit = "tear apart"
+  , iweight  = 1
+  , iaspects = [AddLight $ intToDice n]
+  , ieffects = [RefillHP (- n `div` 2), DropBestWeapon]
+  , ifeature = [Fragile, toLinger 10, Identified]
+  , idesc    = ""
+  , ikit     = []
+  }
+explosionBlast10 = explosionBlast 10
+explosionBlast20 = explosionBlast 20
+firecracker :: Int -> ItemKind
+firecracker n = ItemKind
+  { isymbol  = '*'
+  , iname    = "firecracker"
+  , ifreq    = [("firecracker" <+> tshow n, 1)]
+  , iflavour = zipPlain [stdCol !! (n `mod` length stdCol)]
+  , icount   = intToDice $ 2 * n
+  , irarity  = [(1, 1)]
+  , iverbHit = "crack"
+  , iweight  = 1
+  , iaspects = [AddLight $ intToDice $ n `div` 2]
+  , ieffects = [Burn 1, Explode $ "firecracker" <+> tshow (n - 1)]
+  , ifeature = [ ToThrow $ ThrowMod (n * 10) 20
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+firecracker7 = firecracker 7
+firecracker6 = firecracker 6
+firecracker5 = firecracker 5
+firecracker4 = firecracker 4
+firecracker3 = firecracker 3
+firecracker2 = firecracker 2
+
+-- * Assorted
+
+fragrance = ItemKind
+  { isymbol  = '\''
+  , iname    = "fragrance"
+  , ifreq    = [("fragrance", 1)]
+  , iflavour = zipFancy [Magenta]
+  , icount   = 15
+  , irarity  = [(1, 1)]
+  , iverbHit = "engulf"
+  , iweight  = 1
+  , iaspects = []
+  , ieffects = [Impress]
+  , ifeature = [ toVelocity 13  -- the slowest that travels at least 2 steps
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+pheromone = ItemKind
+  { isymbol  = '\''
+  , iname    = "musky whiff"
+  , ifreq    = [("pheromone", 1)]
+  , iflavour = zipFancy [BrMagenta]
+  , icount   = 8
+  , irarity  = [(1, 1)]
+  , iverbHit = "tempt"
+  , iweight  = 1
+  , iaspects = []
+  , ieffects = [Dominate]
+  , ifeature = [ toVelocity 13  -- the slowest that travels at least 2 steps
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+mistHealing = ItemKind
+  { isymbol  = '\''
+  , iname    = "mist"
+  , ifreq    = [("healing mist", 1)]
+  , iflavour = zipFancy [White]
+  , icount   = 11
+  , irarity  = [(1, 1)]
+  , iverbHit = "revitalize"
+  , iweight  = 1
+  , iaspects = [AddLight 1]
+  , ieffects = [RefillHP 2]
+  , ifeature = [ toVelocity 7  -- the slowest that gets anywhere (1 step only)
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+mistWounding = ItemKind
+  { isymbol  = '\''
+  , iname    = "mist"
+  , ifreq    = [("wounding mist", 1)]
+  , iflavour = zipFancy [White]
+  , icount   = 13
+  , irarity  = [(1, 1)]
+  , iverbHit = "devitalize"
+  , iweight  = 1
+  , iaspects = []
+  , ieffects = [RefillHP (-2)]
+  , ifeature = [ toVelocity 7  -- the slowest that gets anywhere (1 step only)
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+distortion = ItemKind
+  { isymbol  = 'v'
+  , iname    = "vortex"
+  , ifreq    = [("distortion", 1)]
+  , iflavour = zipFancy [White]
+  , icount   = 4
+  , irarity  = [(1, 1)]
+  , iverbHit = "engulf"
+  , iweight  = 1
+  , iaspects = []
+  , ieffects = [Teleport $ 15 + d 10]
+  , ifeature = [ toVelocity 7  -- the slowest that gets anywhere (1 step only)
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+waste = ItemKind
+  { isymbol  = '*'
+  , iname    = "waste"
+  , ifreq    = [("waste", 1)]
+  , iflavour = zipPlain [Brown]
+  , icount   = 10
+  , irarity  = [(1, 1)]
+  , iverbHit = "splosh"
+  , iweight  = 50
+  , iaspects = []
+  , ieffects = [RefillHP (-1)]
+  , ifeature = [ ToThrow $ ThrowMod 28 50
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+glassPiece = ItemKind  -- when blowing up windows
+  { isymbol  = '*'
+  , iname    = "glass piece"
+  , ifreq    = [("glass piece", 1)]
+  , iflavour = zipPlain [BrBlue]
+  , icount   = 17
+  , irarity  = [(1, 1)]
+  , iverbHit = "cut"
+  , iweight  = 10
+  , iaspects = []
+  , ieffects = [Hurt (1 * d 1)]
+  , ifeature = [toLinger 20, Fragile, Identified]
+  , idesc    = ""
+  , ikit     = []
+  }
+smoke = ItemKind  -- when stuff burns out
+  { isymbol  = '\''
+  , iname    = "smoke"
+  , ifreq    = [("smoke", 1), ("firecracker 1", 1)]
+  , iflavour = zipPlain [BrBlack]
+  , icount   = 19
+  , irarity  = [(1, 1)]
+  , iverbHit = "choke"
+  , iweight  = 1
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [ toVelocity 21, Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+boilingWater = ItemKind
+  { isymbol  = '*'
+  , iname    = "boiling water"
+  , ifreq    = [("boiling water", 1)]
+  , iflavour = zipPlain [BrWhite]
+  , icount   = 9
+  , irarity  = [(1, 1)]
+  , iverbHit = "boil"
+  , iweight  = 5
+  , iaspects = []
+  , ieffects = [Burn 1]
+  , ifeature = [toVelocity 50, Fragile, Identified]
+  , idesc    = ""
+  , ikit     = []
+  }
+glue = ItemKind
+  { isymbol  = '*'
+  , iname    = "sticky foam"
+  , ifreq    = [("glue", 1)]
+  , iflavour = zipPlain [BrYellow]
+  , icount   = 14
+  , irarity  = [(1, 1)]
+  , iverbHit = "glue"
+  , iweight  = 20
+  , iaspects = []
+  , ieffects = [Paralyze (3 + d 3)]
+  , ifeature = [toVelocity 40, Fragile, Identified]
+  , idesc    = ""
+  , ikit     = []
+  }
diff --git a/GameDefinition/Content/ModeKind.hs b/GameDefinition/Content/ModeKind.hs
--- a/GameDefinition/Content/ModeKind.hs
+++ b/GameDefinition/Content/ModeKind.hs
@@ -6,7 +6,7 @@
 -- | The type of kinds of game modes for Allure of the Stars.
 module Content.ModeKind ( cdefs ) where
 
-import qualified Data.EnumMap.Strict as EM
+import qualified Data.IntMap.Strict as IM
 
 import Game.LambdaHack.Common.ContentDef
 import Game.LambdaHack.Content.ModeKind
@@ -18,40 +18,71 @@
   , getFreq = mfreq
   , validate = validateModeKind
   , content =
-      [campaign, skirmish, battle, pvp, coop, defense, testCampaign, testSkirmish, testBattle, testPvP, testCoop, testDefense, peekCampaign, peekSkirmish]
+      [campaign, duel, skirmish, ambush, battle, safari, pvp, coop, defense]
   }
-campaign,        skirmish, battle, pvp, coop, defense, testCampaign, testSkirmish, testBattle, testPvP, testCoop, testDefense, peekCampaign, peekSkirmish :: ModeKind
+campaign,        duel, skirmish, ambush, battle, safari, pvp, coop, defense :: ModeKind
 
 campaign = ModeKind
-  { msymbol  = 'r'
+  { msymbol  = 'a'
   , mname    = "campaign"
   , mfreq    = [("campaign", 1)]
   , mplayers = playersCampaign
   , mcaves   = cavesCampaign
+  , mdesc    = "You got stranded looting a once luxurious cruise liner and your current plan is to fight through, gathering your spoils, to the bridge at the giant spaceship's opposite end."
   }
 
+duel = ModeKind
+  { msymbol  = 'u'
+  , mname    = "duel"
+  , mfreq    = [("duel", 1)]
+  , mplayers = playersDuel
+  , mcaves   = cavesSkirmish
+  , mdesc    = "Let's settle the argument about this noble lady outside, in the woody biosphere behind the saloon."
+  }
+
 skirmish = ModeKind
   { msymbol  = 'k'
   , mname    = "skirmish"
   , mfreq    = [("skirmish", 1)]
   , mplayers = playersSkirmish
-  , mcaves   = cavesCombat
+  , mcaves   = cavesSkirmish
+  , mdesc    = "You owe restorative surgery to one of our crew: if we win, we take all you have; if you win, you take ours old giant spaceship (if you still want it when you see it)."
   }
 
+ambush = ModeKind
+  { msymbol  = 'm'
+  , mname    = "ambush"
+  , mfreq    = [("ambush", 1)]
+  , mplayers = playersSkirmish
+  , mcaves   = cavesAmbush
+  , mdesc    = "Conveniently, on the path to the Triton's spaceport, passengers can relax in a shady park."
+  }
+
 battle = ModeKind
   { msymbol  = 'b'
   , mname    = "battle"
   , mfreq    = [("battle", 1)]
   , mplayers = playersBattle
   , mcaves   = cavesBattle
+  , mdesc    = "Not even the unexplained ruin of the largest and tightest security Neptune's moon spaceport will prevent you from claiming your prize."
   }
 
+safari = ModeKind
+  { msymbol  = 'f'
+  , mname    = "safari"
+  , mfreq    = [("safari", 1)]
+  , mplayers = playersSafari
+  , mcaves   = cavesSafari
+  , mdesc    = "In this simulation you'll discover the joys of hunting the most exquisite of Earth's flora and fauna, both animal and semi-intelligent (exit at the bottommost level)."
+  }
+
 pvp = ModeKind
   { msymbol  = 'v'
   , mname    = "PvP"
   , mfreq    = [("PvP", 1)]
   , mplayers = playersPvP
-  , mcaves   = cavesCombat
+  , mcaves   = cavesSkirmish
+  , mdesc    = "(Not usable right now.) This is a fight to the death between two human-controlled teams."
   }
 
 coop = ModeKind
@@ -60,6 +91,7 @@
   , mfreq    = [("Coop", 1)]
   , mplayers = playersCoop
   , mcaves   = cavesCampaign
+  , mdesc    = "(This mode is intended solely for automated testing.)"
   }
 
 defense = ModeKind
@@ -68,107 +100,82 @@
   , mfreq    = [("defense", 1)]
   , mplayers = playersDefense
   , mcaves   = cavesCampaign
-  }
-
-testCampaign = ModeKind
-  { msymbol  = 't'
-  , mname    = "testCampaign"
-  , mfreq    = [("testCampaign", 1)]
-  , mplayers = playersTestCampaign
-  , mcaves   = cavesCampaign
-  }
-
-testSkirmish = ModeKind
-  { msymbol  = 't'
-  , mname    = "testSkirmish"
-  , mfreq    = [("testSkirmish", 1)]
-  , mplayers = playersTestSkirmish
-  , mcaves   = cavesCombat
-  }
-
-testBattle = ModeKind
-  { msymbol  = 't'
-  , mname    = "testBattle"
-  , mfreq    = [("testBattle", 1)]
-  , mplayers = playersTestBattle
-  , mcaves   = cavesBattle
-  }
-
-testPvP = ModeKind
-  { msymbol  = 't'
-  , mname    = "testPvP"
-  , mfreq    = [("testPvP", 1)]
-  , mplayers = playersTestPvP
-  , mcaves   = cavesCombat
-  }
-
-testCoop = ModeKind
-  { msymbol  = 't'
-  , mname    = "testCoop"
-  , mfreq    = [("testCoop", 1)]
-  , mplayers = playersTestCoop
-  , mcaves   = cavesCampaign
-  }
-
-testDefense = ModeKind
-  { msymbol  = 't'
-  , mname    = "testDefense"
-  , mfreq    = [("testDefense", 1)]
-  , mplayers = playersTestDefense
-  , mcaves   = cavesCampaign
-  }
-
-peekCampaign = ModeKind
-  { msymbol  = 'p'
-  , mname    = "peekCampaign"
-  , mfreq    = [("peekCampaign", 1)]
-  , mplayers = playersPeekCampaign
-  , mcaves   = cavesCampaign
-  }
-
-peekSkirmish = ModeKind
-  { msymbol  = 'p'
-  , mname    = "peekSkirmish"
-  , mfreq    = [("peekSkirmish", 1)]
-  , mplayers = playersPeekSkirmish
-  , mcaves   = cavesCombat
+  , mdesc    = "Don't let the half-witted humans derail your operation and flee, like the puny, naked, tentacled-less beasts that they are!"
   }
 
 
-playersCampaign, playersSkirmish, playersBattle, playersPvP, playersCoop, playersDefense, playersTestCampaign, playersTestSkirmish, playersTestBattle, playersTestPvP, playersTestCoop, playersTestDefense, playersPeekCampaign, playersPeekSkirmish :: Players
+playersCampaign, playersDuel, playersSkirmish, playersBattle, playersSafari, playersPvP, playersCoop, playersDefense :: Players
 
 playersCampaign = Players
-  { playersList = [ playerHero {playerInitial = 1}
-                  , playerMonster
+  { playersList = [ playerHero
+                  , playerAlien
                   , playerAnimal
                   , playerRobot ]
   , playersEnemy = [ ("Spacefarer Crew", "Alien Hierarchy")
                    , ("Spacefarer Crew", "Animal Kingdom")
                    , ("Spacefarer Crew", "Robot Anarchy") ]
-  , playersAlly = [] }
+ , playersAlly = [ ("Alien Hierarchy", "Animal Kingdom")
+                 , ("Alien Hierarchy", "Robot Anarchy")
+                 , ("Robot Anarchy", "Animal Kingdom") ] }
 
-playersSkirmish = Players
-  { playersList = [ playerHero {playerName = "White"}
-                  , playerAntiHero {playerName = "Purple"}
+playersDuel = Players
+  { playersList = [ playerHero { playerName = "Spacefarer Crew"
+                               , playerInitial = 1 }
+                  , playerAntiHero { playerName = "Red Collars"
+                                   , playerInitial = 1 }
                   , playerHorror ]
-  , playersEnemy = [ ("White", "Purple")
-                   , ("White", "Horror Den")
-                   , ("Purple", "Horror Den") ]
+  , playersEnemy = [ ("Spacefarer Crew", "Red Collars")
+                   , ("Spacefarer Crew", "Horror Den")
+                   , ("Red Collars", "Horror Den") ]
   , playersAlly = [] }
 
+playersSkirmish = playersDuel
+  { playersList = [ playerHero {playerName = "Spacefarer Crew"}
+                  , playerAntiHero {playerName = "Red Collars"}
+                  , playerHorror ] }
+
 playersBattle = Players
   { playersList = [ playerHero {playerInitial = 5}
-                  , playerMonster { playerInitial = 10
-                                  , playerSpawn = 0 }
-                  , playerAnimal { playerInitial = 10
-                                 , playerSpawn = 0 }
-                  , playerRobot { playerInitial = 10
-                                , playerSpawn = 0 } ]
+                  , playerAlien { playerInitial = 15
+                                , playerIsSpawn = False }
+                  , playerAnimal { playerInitial = 5
+                                 , playerIsSpawn = False }
+                  , playerRobot { playerInitial = 5
+                                 , playerIsSpawn = False } ]
   , playersEnemy = [ ("Spacefarer Crew", "Alien Hierarchy")
                    , ("Spacefarer Crew", "Animal Kingdom")
                    , ("Spacefarer Crew", "Robot Anarchy") ]
-  , playersAlly = [] }
+ , playersAlly = [ ("Alien Hierarchy", "Animal Kingdom")
+                 , ("Alien Hierarchy", "Robot Anarchy")
+                 , ("Robot Anarchy", "Animal Kingdom") ] }
 
+playersSafari = Players
+  { playersList = [ playerAlien { playerName = "Alien Tourist Office"
+                                , playerIsSpawn = False
+                                , playerEntry = 4
+                                , playerInitial = 10
+                                , playerAI = False
+                                , playerUI = True }
+                  , playerCivilian { playerName = "Hunam Convict Pack"
+                                   , playerEntry = 4 }
+                  , playerAnimal { playerName =
+                                     "Animal Magnificent Specimen Variety"
+                                 , playerIsSpawn = False
+                                 , playerEntry = 7
+                                 , playerInitial = 7 }
+                  , playerAnimal { playerName =
+                                     "Animal Exquisite Herds and Packs"
+                                 , playerIsSpawn = False
+                                 , playerEntry = 10
+                                 , playerInitial = 20 } ]
+  , playersEnemy = [ ("Alien Tourist Office", "Hunam Convict Pack")
+                   , ("Alien Tourist Office",
+                      "Animal Magnificent Specimen Variety")
+                   , ("Alien Tourist Office",
+                      "Animal Exquisite Herds and Packs") ]
+  , playersAlly = [( "Animal Magnificent Specimen Variety"
+                   , "Animal Exquisite Herds and Packs" )] }
+
 playersPvP = Players
   { playersList = [ playerHero {playerName = "Red"}
                   , playerHero {playerName = "Blue"}
@@ -179,186 +186,133 @@
   , playersAlly = [] }
 
 playersCoop = Players
-  { playersList = [ playerHero { playerName = "Coral"
-                               , playerInitial = 1 }
-                  , playerHero { playerName = "Amber"
-                               , playerInitial = 1 }
-                  , playerMonster
-                  , playerAnimal
+  { playersList = [ playerAntiHero { playerName = "Coral" }
+                  , playerAntiHero { playerName = "Amber"
+                                   , playerLeader = False }
+                  , playerAntiHero { playerName = "Green" }
+                  , playerAnimal { playerUI = True }
+                  , playerAlien { playerName = "Alien Hierarchy" }
+                  , playerAlien { playerName = "Leaderless Alien Hierarchy"
+                                  , playerLeader = False }
                   , playerRobot ]
   , playersEnemy = [ ("Coral", "Alien Hierarchy")
-                   , ("Coral", "Animal Kingdom")
-                   , ("Coral", "Robot Anarchy")
                    , ("Amber", "Alien Hierarchy")
-                   , ("Amber", "Animal Kingdom")
-                   , ("Amber", "Robot Anarchy") ]
-  , playersAlly = [("Coral", "Amber")] }
+                   , ("Animal Kingdom", "Leaderless Alien Hierarchy") ]
+  , playersAlly = [ ("Coral", "Amber")
+                  , ("Coral", "Green")
+                  , ("Amber", "Green")
+                  , ("Green", "Animal Kingdom")
+                  , ("Green", "Robot Anarchy")
+                  , ("Green", "Alien Hierarchy")
+                  , ("Green", "Leaderless Alien Hierarchy") ] }
 
 playersDefense = Players
-  { playersList = [ playerMonster { playerInitial = 1
-                                  , playerAiLeader = False
-                                  , playerHuman = True
-                                  , playerUI = True }
+  { playersList = [ playerAlien { playerInitial = 1
+                                , playerAI = False
+                                , playerUI = True }
+                  , playerAntiHero { playerName = "Yellow"
+                                   , playerInitial = 10 }
                   , playerAnimal
-                  , playerRobot
-                  , playerAntiHero {playerName = "Green"}
-                  , playerAntiHero {playerName = "Yellow"}
-                  , playerAntiHero {playerName = "Cyan"} ]
-  , playersEnemy = [ ("Green", "Alien Hierarchy")
-                   , ("Green", "Animal Kingdom")
-                   , ("Green", "Robot Anarchy")
-                   , ("Yellow", "Alien Hierarchy")
+                  , playerRobot ]
+  , playersEnemy = [ ("Yellow", "Alien Hierarchy")
                    , ("Yellow", "Animal Kingdom")
-                   , ("Yellow", "Robot Anarchy")
-                   , ("Cyan", "Alien Hierarchy")
-                   , ("Cyan", "Animal Kingdom")
-                   , ("Cyan", "Robot Anarchy") ]
-  , playersAlly = [ ("Green", "Yellow")
-                  , ("Green", "Cyan")
-                  , ("Yellow", "Cyan") ] }
-
-playersTestCampaign = playersCampaign
-  { playersList = [ playerHero { playerInitial = 5
-                               , playerAiLeader = True
-                               , playerHuman = False }
-                  , playerMonster
-                  , playerAnimal
-                  , playerRobot ] }
-
-playersTestSkirmish = playersSkirmish
-  { playersList = [ playerHero { playerName = "White"
-                               , playerAiLeader = True
-                               , playerHuman = False }
-                  , playerAntiHero { playerName = "Purple" }
-                  , playerHorror ] }
-
-playersTestBattle = playersBattle
-  { playersList = [ playerHero { playerInitial = 5
-                               , playerAiLeader = True
-                               , playerHuman = False }
-                  , playerMonster { playerInitial = 10
-                                  , playerSpawn = 0 }
-                  , playerAnimal { playerInitial = 10
-                                 , playerSpawn = 0 }
-                  , playerRobot { playerInitial = 10
-                                , playerSpawn = 0 } ] }
-
-playersTestPvP = playersPvP
-  { playersList = [ playerHero { playerName = "Red"
-                               , playerAiLeader = True
-                               , playerHuman = False }
-                  , playerHero { playerName = "Blue"
-                               , playerAiLeader = True
-                               , playerHuman = False }
-                  , playerHorror ] }
-
-playersTestCoop = playersCoop
-  { playersList = [ playerHero { playerName = "Coral"
-                               , playerAiLeader = True
-                               , playerHuman = False }
-                  , playerHero { playerName = "Amber"
-                               , playerAiLeader = True
-                               , playerHuman = False }
-                  , playerMonster
-                  , playerAnimal
-                  , playerRobot ] }
-
-playersTestDefense = playersDefense
-  { playersList = [ playerMonster { playerInitial = 1
-                                  , playerUI = True }
-                  , playerAnimal
-                  , playerRobot
-                  , playerAntiHero {playerName = "Green"}
-                  , playerAntiHero {playerName = "Yellow"}
-                  , playerAntiHero {playerName = "Cyan"} ] }
-
-playersPeekCampaign = playersCampaign
-  { playersList = [ playerHero {playerInitial = 1}
-                  , playerMonster {playerUI = True}
-                  , playerAnimal
-                  , playerRobot ] }
-
-playersPeekSkirmish = playersSkirmish
-  { playersList = [ playerHero {playerName = "White"}
-                  , playerAntiHero { playerName = "Purple"
-                                   , playerUI = True }
-                  , playerHorror ] }
-
+                   , ("Yellow", "Robot Anarchy") ]
+  , playersAlly = [ ("Alien Hierarchy", "Animal Kingdom")
+                  , ("Alien Hierarchy", "Robot Anarchy")
+                  , ("Robot Anarchy", "Animal Kingdom") ] }
 
-playerHero, playerAntiHero, playerMonster, playerAnimal, playerRobot, playerHorror :: Player
+playerHero, playerAntiHero, playerCivilian, playerAlien, playerAnimal, playerRobot, playerHorror :: Player
 
 playerHero = Player
   { playerName = "Spacefarer Crew"
   , playerFaction = "hero"
-  , playerSpawn = 0
-  , playerEntry = toEnum 1
+  , playerIsSpawn = False
+  , playerIsHero = True
+  , playerEntry = 1
   , playerInitial = 3
-  , playerAiLeader = False
-  , playerAiOther = True
-  , playerHuman = True
+  , playerLeader = True
+  , playerAI = False
   , playerUI = True
   }
 
 playerAntiHero = playerHero
-  { playerAiLeader = True
-  , playerHuman = False
+  { playerAI = True
   , playerUI = False
   }
 
-playerMonster = Player
+playerCivilian = Player
+  { playerName = "Civilian Crowd"
+  , playerFaction = "civilian"
+  , playerIsSpawn = False
+  , playerIsHero = False
+  , playerEntry = 1
+  , playerInitial = 3
+  , playerLeader = False  -- unorganized
+  , playerAI = True
+  , playerUI = False
+  }
+
+playerAlien = Player
   { playerName = "Alien Hierarchy"
   , playerFaction = "alien"
-  , playerSpawn = 20
-  , playerEntry = toEnum 5
+  , playerIsSpawn = True
+  , playerIsHero = False
+  , playerEntry = 4
   , playerInitial = 3
-  , playerAiLeader = True
-  , playerAiOther = True
-  , playerHuman = False
+  , playerLeader = True
+  , playerAI = True
   , playerUI = False
   }
 
 playerAnimal = Player
   { playerName = "Animal Kingdom"
   , playerFaction = "animal"
-  , playerSpawn = 50
-  , playerEntry = toEnum 3
+  , playerIsSpawn = True
+  , playerIsHero = False
+  , playerEntry = 2
   , playerInitial = 3
-  , playerAiLeader = True
-  , playerAiOther = True
-  , playerHuman = False
+  , playerLeader = False
+  , playerAI = True
   , playerUI = False
   }
 
 playerRobot = Player
   { playerName = "Robot Anarchy"
   , playerFaction = "robot"
-  , playerSpawn = 10
-  , playerEntry = toEnum 4
+  , playerIsSpawn = True
+  , playerIsHero = False
+  , playerEntry = 3
   , playerInitial = 3
-  , playerAiLeader = True
-  , playerAiOther = True
-  , playerHuman = False
+  , playerLeader = False
+  , playerAI = True
   , playerUI = False
   }
 
 playerHorror = Player
   { playerName = "Horror Den"
   , playerFaction = "horror"
-  , playerSpawn = 0
-  , playerEntry = toEnum 1
+  , playerIsSpawn = False
+  , playerIsHero = False
+  , playerEntry = 1
   , playerInitial = 0
-  , playerAiLeader = True
-  , playerAiOther = True
-  , playerHuman = False
+  , playerLeader = False
+  , playerAI = True
   , playerUI = False
   }
 
 
-cavesCampaign, cavesCombat, cavesBattle :: Caves
+cavesCampaign, cavesSkirmish, cavesAmbush, cavesBattle, cavesSafari :: Caves
 
-cavesCampaign = EM.fromList [ (toEnum 1, ("caveRogue", Nothing))
-                            , (toEnum 12, ("caveNoise", Just True))]
+cavesCampaign = IM.fromList $ [(1, ("caveRogue", Nothing))]
+                              ++ zip [2..11] (repeat ("dng", Nothing))
+                              ++ [(12, ("caveNoise", Just True))]
 
-cavesCombat = EM.fromList [(toEnum 3, ("caveCombat", Nothing))]
+cavesSkirmish = IM.fromList [(3, ("caveSkirmish", Nothing))]
 
-cavesBattle = EM.fromList [(toEnum (-3), ("caveBattle", Nothing))]
+cavesAmbush = IM.fromList [(5, ("caveAmbush", Nothing))]
+
+cavesBattle = IM.fromList [(3, ("caveBattle", Nothing))]
+
+cavesSafari = IM.fromList [ (4, ("caveSafari1", Nothing))
+                          , (7, ("caveSafari2", Nothing))
+                          , (10, ("caveSafari3", Just False)) ]
diff --git a/GameDefinition/Content/PlaceKind.hs b/GameDefinition/Content/PlaceKind.hs
--- a/GameDefinition/Content/PlaceKind.hs
+++ b/GameDefinition/Content/PlaceKind.hs
@@ -16,22 +16,170 @@
   , getFreq = pfreq
   , validate = validatePlaceKind
   , content =
-      [rect, oval, ovalFloor, ovalSquare, colonnade, colonnadeWide, maze,  maze2, maze3, mazeBig, mazeBig2, mazeBig3, cells]
+      [rect, ruin, collapsed, collapsed2, collapsed3, collapsed4, pillar, pillarC, pillar3, lampPost, lampPost2, lampPost3, lampPost4, treeShade, treeShade2, treeShade3, oval, ovalFloor, ovalSquare, colonnade, colonnadeWide, maze,  maze2, maze3, mazeBig, mazeBig2, mazeBig3, cells]
   }
-rect,        oval, ovalFloor, ovalSquare, colonnade, colonnadeWide, maze,  maze2, maze3, mazeBig, mazeBig2, mazeBig3, cells :: PlaceKind
+rect,        ruin, collapsed, collapsed2, collapsed3, collapsed4, pillar, pillarC, pillar3, lampPost, lampPost2, lampPost3, lampPost4, treeShade, treeShade2, treeShade3, oval, ovalFloor, ovalSquare, colonnade, colonnadeWide, maze,  maze2, maze3, mazeBig, mazeBig2, mazeBig3, cells :: PlaceKind
 
 rect = PlaceKind  -- Valid for any nonempty area, hence low frequency.
   { psymbol  = 'r'
   , pname    = "room"
-  , pfreq    = [("rogue", 100)]
+  , pfreq    = [("rogue", 100), ("ambush", 8)]
+  , prarity  = [(1, 1)]
   , pcover   = CStretch
   , pfence   = FWall
   , ptopLeft = ["."]
+  , poverride = []
   }
+ruin = PlaceKind
+  { psymbol  = 'R'
+  , pname    = "ruin"
+  , pfreq    = [("ambush", 17), ("battle", 100)]
+  , prarity  = [(1, 1)]
+  , pcover   = CStretch
+  , pfence   = FWall
+  , ptopLeft = ["X"]
+  , poverride = []
+  }
+collapsed = PlaceKind
+  { psymbol  = 'c'
+  , pname    = "collapsed cavern"
+  , pfreq    = [("noise", 1)]
+  , prarity  = [(1, 1)]
+  , pcover   = CStretch
+  , pfence   = FNone
+  , ptopLeft = ["#"]
+  , poverride = [('#', "doorlessWallOver_#")]
+  }
+collapsed2 = collapsed
+  { pfreq    = [("noise", 100), ("battle", 50)]
+  , ptopLeft = [ "XX#"
+               , "X##"
+               ]
+  }
+collapsed3 = collapsed
+  { pfreq    = [("noise", 200), ("battle", 50)]
+  , ptopLeft = [ "XXX#"
+               , "X###"
+               ]
+  }
+collapsed4 = collapsed
+  { pfreq    = [("noise", 400), ("battle", 200)]
+  , ptopLeft = [ "XXX#"
+               , "XXX#"
+               , "X###"
+               ]
+  }
+pillar = PlaceKind
+  { psymbol  = 'p'
+  , pname    = "pillar room"
+  , pfreq    = [("rogue", 1000)]  -- larger rooms require support pillars
+  , prarity  = [(1, 1)]
+  , pcover   = CStretch
+  , pfence   = FWall
+  , ptopLeft = [ "...."
+               , ".#.."
+               , "...."
+               , "...."
+               ]
+  , poverride = []
+  }
+pillarC = pillar
+  { ptopLeft = [ ".#.."
+               , "#..."
+               , "...."
+               , "...."
+               ]
+  }
+pillar3 = pillar
+  { ptopLeft = [ "&.#."
+               , "...."
+               , "#..."
+               , "...."
+               ]
+  }
+colonnade = PlaceKind
+  { psymbol  = 'c'
+  , pname    = "colonnade"
+  , pfreq    = [("rogue", 60)]
+  , prarity  = [(1, 1)]
+  , pcover   = CAlternate
+  , pfence   = FFloor
+  , ptopLeft = [ ".#"
+               , "#."
+               ]
+  , poverride = []
+  }
+colonnadeWide = colonnade
+  { pfence   = FWall
+  , ptopLeft = [ ".."
+               , ".#"
+               ]
+  }
+lampPost = PlaceKind
+  { psymbol  = 'l'
+  , pname    = "lamp post"
+  , pfreq    = [("ambush", 30), ("battle", 10)]
+  , prarity  = [(1, 1)]
+  , pcover   = CVerbatim
+  , pfence   = FNone
+  , ptopLeft = [ "X.X"
+               , ".O."
+               , "X.X"
+               ]
+  , poverride = [('O', "lampPostOver_O")]
+  }
+lampPost2 = lampPost
+  { ptopLeft = [ "..."
+               , ".O."
+               , "..."
+               ]
+  }
+lampPost3 = lampPost
+  { ptopLeft = [ "XX.XX"
+               , "X...X"
+               , "..O.."
+               , "X...X"
+               , "XX.XX"
+               ]
+  }
+lampPost4 = lampPost
+  { ptopLeft = [ "X...X"
+               , "....."
+               , "..O.."
+               , "....."
+               , "X...X"
+               ]
+  }
+treeShade = PlaceKind
+  { psymbol  = 't'
+  , pname    = "tree shade"
+  , pfreq    = [("skirmish", 100)]
+  , prarity  = [(1, 1)]
+  , pcover   = CVerbatim
+  , pfence   = FNone
+  , ptopLeft = [ "sss"
+               , "XOs"
+               , "XXs"
+               ]
+  , poverride = [('O', "treeShadeOver_O"), ('s', "treeShadeOver_s")]
+  }
+treeShade2 = treeShade
+  { ptopLeft = [ "sss"
+               , "XOs"
+               , "Xss"
+               ]
+  }
+treeShade3 = treeShade
+  { ptopLeft = [ "sss"
+               , "sOs"
+               , "XXs"
+               ]
+  }
 oval = PlaceKind
   { psymbol  = 'o'
   , pname    = "oval room"
   , pfreq    = [("rogue", 1000)]
+  , prarity  = [(1, 1)]
   , pcover   = CStretch
   , pfence   = FWall
   , ptopLeft = [ "####.."
@@ -41,6 +189,7 @@
                , "......"
                , "......"
                ]
+  , poverride = []
   }
 ovalFloor = oval  -- Without outer solid fence, visible from outside.
   { pfreq    = [("rogue", 10000)]
@@ -61,27 +210,11 @@
                , "+...."
                ]
   }
-colonnade = PlaceKind
-  { psymbol  = 'c'
-  , pname    = "colonnade"
-  , pfreq    = [("rogue", 1000)]
-  , pcover   = CAlternate
-  , pfence   = FFloor
-  , ptopLeft = [ ".#"
-               , "#."
-               ]
-  }
-colonnadeWide = colonnade
-  { pfreq    = [("rogue", 50)]
-  , pfence   = FWall
-  , ptopLeft = [ ".."
-               , ".#"
-               ]
-  }
 maze = PlaceKind
   { psymbol  = 'm'
   , pname    = "maze"
   , pfreq    = [("rogue", 20)]
+  , prarity  = [(1, 1)]
   , pcover   = CStretch
   , pfence   = FNone
   , ptopLeft = [ "#.#.##"
@@ -89,6 +222,7 @@
                , "#.##.#"
                , "#.#.#."
                ]
+  , poverride = []
   }
 maze2 = maze
   { ptopLeft = [ "###.##"
@@ -136,13 +270,15 @@
   { psymbol  = '#'
   , pname    = "cells"
   , pfreq    = [("rogue", 30)]
+  , prarity  = [(1, 1)]
   , pcover   = CReflect
   , pfence   = FWall
   , ptopLeft = [ "..#"
                , "..#"
                , "##."
                ]
+  , poverride = []
   }
--- TODO: obtain all the reet as places nested within places.
+-- TODO: obtain all the rest as places nested within places.
 -- 3 places are enough, with 1 or 2 tiles between places,
 -- on all sides, only vertical, only horizontal,
diff --git a/GameDefinition/Content/RuleKind.hs b/GameDefinition/Content/RuleKind.hs
--- a/GameDefinition/Content/RuleKind.hs
+++ b/GameDefinition/Content/RuleKind.hs
@@ -7,7 +7,6 @@
 -- | Game rules and assorted game setup data for Allure of the Stars.
 module Content.RuleKind ( cdefs ) where
 
-import Control.Arrow (first)
 import Language.Haskell.TH.Syntax
 import System.FilePath
 
@@ -15,10 +14,6 @@
 import qualified Paths_Allure as Self (getDataFileName, version)
 
 import Game.LambdaHack.Common.ContentDef
-import qualified Game.LambdaHack.Common.Effect as Effect
-import qualified Game.LambdaHack.Common.Feature as F
-import Game.LambdaHack.Common.HumanCmd
-import qualified Game.LambdaHack.Common.Key as K
 import Game.LambdaHack.Content.RuleKind
 
 cdefs :: ContentDef RuleKind
@@ -37,22 +32,19 @@
   , rname          = "standard Allure of the Stars ruleset"
   , rfreq          = [("standard", 100)]
   -- Check whether one position is accessible from another.
-  -- Precondition: the two positions are next to each other.
+  -- Precondition: the two positions are next to each other
+  -- and the target tile is walkable. 
   -- TODO: in the future check flying for chasms, swimming for water, etc.
   , raccessible    = Nothing
   , raccessibleDoor = Nothing
   , rtitle         = "Allure of the Stars"
   , rpathsDataFile = Self.getDataFileName
   , rpathsVersion  = Self.version
-  , ritemMelee     = "/|\\"
-  , ritemRanged    = "}{"
-  -- Wasting weapons and armour would be too cruel to the player.
-  , ritemProject   = "!?,-~}{"
   -- The strings containing the default configuration file
   -- included from config.ui.default.
   , rcfgUIName = "config.ui"
   , rcfgUIDefault = $(do
-      let path = "GameDefinition" </> "config.ui" ++ ".default"
+      let path = "GameDefinition" </> "config.ui" <.> "default"
       qAddDependentFile path
       x <- qRunIO (readFile path)
       lift x)
@@ -76,127 +68,11 @@
       qAddDependentFile path
       x <- qRunIO (readFile path)
       lift x)
-  , rhumanCommands = map (first K.mkKM)
-      -- All commands are defined here, except some movement and leader picking
-      -- commands. All commands are shown on help screens except debug commands
-      -- and macros with empty descriptions.
-      -- The order below determines the order on the help screens.
-      -- Remember to put commands that show information (e.g., enter targeting
-      -- mode) first.
-
-      -- Main Menu, which apart of these includes a few extra commands
-      [ ("CTRL-x", (CmdMenu, GameExit))
-      , ("CTRL-r", (CmdMenu, GameRestart "campaign"))
-      , ("CTRL-k", (CmdMenu, GameRestart "skirmish"))
-      , ("CTRL-v", (CmdMenu, GameRestart "PvP"))
-      , ("CTRL-o", (CmdMenu, GameRestart "Coop"))
-      , ("CTRL-e", (CmdMenu, GameRestart "defense"))
-      , ("CTRL-d", (CmdMenu, GameDifficultyCycle))
-
-      -- Movement and terrain alteration
-      , ("less", (CmdMove, TriggerTile
-           [ TriggerFeature { verb = "ascend"
-                            , object = "a level"
-                            , feature = F.Cause (Effect.Ascend 1) }
-           , TriggerFeature { verb = "exit"
-                            , object = "spaceship"
-                            , feature = F.Cause (Effect.Escape 1) } ]))
-      , ("CTRL-less", (CmdMove, TriggerTile
-           [ TriggerFeature { verb = "ascend"
-                            , object = "10 levels"
-                            , feature = F.Cause (Effect.Ascend 10) } ]))
-      , ("greater", (CmdMove, TriggerTile
-           [ TriggerFeature { verb = "descend"
-                            , object = "a level"
-                            , feature = F.Cause (Effect.Ascend (-1)) }
-           , TriggerFeature { verb = "exit"
-                            , object = "spaceship"
-                            , feature = F.Cause (Effect.Escape (-1)) } ]))
-      , ("CTRL-greater", (CmdMove, TriggerTile
-           [ TriggerFeature { verb = "descend"
-                            , object = "10 levels"
-                            , feature = F.Cause (Effect.Ascend (-10)) } ]))
-      , ("CTRL-semicolon", (CmdMove, StepToTarget))
-      , ("semicolon", (CmdMove, Macro "go to target for 100 steps"
-                                      ["CTRL-semicolon", "P"]))
-      , ("x", (CmdMove, Macro "explore the closest unknown spot"
-                              [ "BackSpace"
-                              , "CTRL-question", "CTRL-semicolon", "P" ]))
-      , ("X", (CmdMove, Macro "autoexplore 100 times"
-                              [ "BackSpace"
-                              , "'", "CTRL-question", "CTRL-semicolon", "'"
-                              , "P" ]))
-      , ("R", (CmdMove, Macro "rest (wait 100 times)" ["KP_Begin", "P"]))
-      , ("c", (CmdMove, AlterDir
-           [ AlterFeature { verb = "close"
-                          , object = "door"
-                          , feature = F.CloseTo "closed door" } ]))
-
-      -- Inventory and items
-      , ("I", (CmdItem, Inventory))
-      , ("g", (CmdItem, Pickup))
-      , ("d", (CmdItem, Drop))
-      , ("q", (CmdItem, Apply [ApplyItem { verb = "quaff"
-                                         , object = "drink"
-                                         , symbol = '!' }]))
-      , ("r", (CmdItem, Apply [ApplyItem { verb = "read"
-                                         , object = "tablet"
-                                         , symbol = '?' }]))
-      , ("a", (CmdItem, Apply [ApplyItem { verb = "apply"
-                                         , object = "consumable"
-                                         , symbol = ' ' }]))
--- a: Apply [ApplyItem {verb = "apply", object = "consumable", symbol = ' '}, ApplyItem {verb = "eat", object = "food", symbol = ','}, ApplyItem {verb = "activate", object = "emitter", symbol = '_'}, ApplyItem {verb = "use", object = "tool", symbol = '~'}, ApplyItem {verb = "quaff", object = "drink", symbol = '!'}, ApplyItem {verb = "read", object = "tablet", symbol = '?'}]
-      , ("t", (CmdItem, Project [ ApplyItem { verb = "throw"
-                                            , object = "projectile"
-                                            , symbol = '}' }
-                                , ApplyItem { verb = "throw"
-                                            , object = "projectile"
-                                            , symbol = '{' } ]))
-      , ("z", (CmdItem, Project [ ApplyItem { verb = "zap"
-                                            , object = "mechanism"
-                                            , symbol = '-' } ]))
-      , ("f", (CmdItem, Project [ ApplyItem { verb = "fling"
-                                            , object = "missile"
-                                            , symbol = ' ' } ]))
--- f: Project [ApplyItem {verb = "fling", object = "missile", symbol = ' '}, ApplyItem {verb = "throw", object = "projectile", symbol = '}'}, ApplyItem {verb = "throw", object = "projectile", symbol = '{'}, ApplyItem {verb = "zap", object = "mechanism", symbol = '-'}]
-
-      -- Targeting
-      , ("asterisk", (CmdTgt, TgtEnemy))
-      , ("slash", (CmdTgt, TgtFloor))
-      , ("plus", (CmdTgt, EpsIncr True))
-      , ("minus", (CmdTgt, EpsIncr False))
-      , ("BackSpace", (CmdTgt, TgtClear))
-      , ("CTRL-question", (CmdTgt, TgtUnknown))
-      , ("CTRL-I", (CmdTgt, TgtItem))
-      , ("CTRL-braceleft", (CmdTgt, TgtStair True))
-      , ("CTRL-braceright", (CmdTgt, TgtStair False))
-
-      -- Assorted
-      , ("question", (CmdMeta, Help))
-      , ("D", (CmdMeta, History))
-      , ("T", (CmdMeta, MarkSuspect))
-      , ("V", (CmdMeta, MarkVision))
-      , ("S", (CmdMeta, MarkSmell))
-      , ("Tab", (CmdMeta, MemberCycle))
-      , ("ISO_Left_Tab", (CmdMeta, MemberBack))
-      , ("equal", (CmdMeta, SelectActor))
-      , ("underscore", (CmdMeta, SelectNone))
-      , ("p", (CmdMeta, Repeat 1))
-      , ("P", (CmdMeta, Repeat 100))
-      , ("CTRL-p", (CmdMeta, Repeat 1000))
-      , ("apostrophe", (CmdMeta, Record))
-      , ("space", (CmdMeta, Clear))
-      , ("Escape", (CmdMeta, Cancel))
-      , ("Return", (CmdMeta, Accept))
-
-      -- Debug and others not to display in help screens
-      , ("CTRL-s", (CmdDebug, GameSave))
-      , ("CTRL-y", (CmdDebug, Resend))
-      ]
   , rfirstDeathEnds = False
-  , rfovMode = Digital 12
+  , rfovMode = Digital
   , rsaveBkpClips = 500
   , rleadLevelClips = 100
   , rscoresFile = "scores"
   , rsavePrefix = "save"
+  , rsharedStash = True
   }
diff --git a/GameDefinition/Content/TileKind.hs b/GameDefinition/Content/TileKind.hs
--- a/GameDefinition/Content/TileKind.hs
+++ b/GameDefinition/Content/TileKind.hs
@@ -7,6 +7,7 @@
 module Content.TileKind ( cdefs ) where
 
 import Control.Arrow (first)
+import Data.Maybe
 import Data.Text (Text)
 import qualified Data.Text as T
 
@@ -24,17 +25,17 @@
   , getFreq = tfreq
   , validate = validateTileKind
   , content =
-      [wall, wallCache, hardRock, oriel, pillar, tree, wallSuspect, doorClosed, doorOpen, stairsUp, stairsDown, escapeUp, escapeDown, liftUp, lift, liftDown, unknown, floorCorridorLit, floorActorLit, floorItemLit, floorActorItemLit, floorRedLit, floorBlueLit, floorGreenLit]
+      [wall, wallCache, hardRock, doorlessWall, oriel, pillar, lampPost, burningBush, bush, tree, wallSuspect, doorClosed, doorOpen, stairsUp, stairsDown, escapeUp, escapeDown, liftUp, lift, liftDown, unknown, floorCorridorLit, floorActorLit, floorItemLit, floorActorItemLit, floorArenaShade, floorRedLit, floorBlueLit, floorGreenLit]
       ++ map makeDarkColor [floorCorridorLit, floorActorLit, floorItemLit, floorActorItemLit]
   }
-wall,        wallCache, hardRock, oriel, pillar, tree, wallSuspect, doorClosed, doorOpen, stairsUp, stairsDown, escapeUp, escapeDown, liftUp, lift, liftDown, unknown, floorCorridorLit, floorActorLit, floorItemLit, floorActorItemLit, floorRedLit, floorBlueLit, floorGreenLit :: TileKind
+wall,        wallCache, hardRock, doorlessWall, oriel, pillar, lampPost, burningBush, bush, tree, wallSuspect, doorClosed, doorOpen, stairsUp, stairsDown, escapeUp, escapeDown, liftUp, lift, liftDown, unknown, floorCorridorLit, floorActorLit, floorItemLit, floorActorItemLit, floorArenaShade, floorRedLit, floorBlueLit, floorGreenLit :: TileKind
 
 wall = TileKind
   { tsymbol  = '#'
   , tname    = "granite wall"
   , tfreq    = [ ("fillerWall", 1), ("cachable", 70)
                , ("legendLit", 100), ("legendDark", 100)
-               , ("noiseSet", 55) ]
+               , ("noiseSet", 100), ("battleSet", 250) ]
   , tcolor   = BrWhite
   , tcolor2  = defFG
   , tfeature = [HideAs "suspect wall"]
@@ -46,7 +47,7 @@
                , ("legendLit", 100), ("legendDark", 100) ]
   , tcolor   = BrWhite
   , tcolor2  = defFG
-  , tfeature = [Suspect, Cause $ Effect.CreateItem 1, ChangeTo "cachable"]
+  , tfeature = [Cause $ Effect.CreateItem 1, ChangeTo "cachable"]
   }
 hardRock = TileKind
   { tsymbol  = '#'
@@ -56,6 +57,14 @@
   , tcolor2  = BrBlack
   , tfeature = [Impenetrable]
   }
+doorlessWall = TileKind
+  { tsymbol  = '#'
+  , tname    = "granite wall"
+  , tfreq    = [("doorlessWallOver_#", 100)]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
+  , tfeature = []
+  }
 oriel = TileKind
   { tsymbol  = '\''
   , tname    = "oriel"
@@ -68,15 +77,39 @@
   { tsymbol  = 'O'
   , tname    = "rock"
   , tfreq    = [ ("legendLit", 100), ("legendDark", 100)
-               , ("combatSet", 3), ("battleSet", 9) ]
+               , ("skirmishSet", 5) ]
   , tcolor   = BrWhite
   , tcolor2  = defFG
   , tfeature = []
   }
+lampPost = TileKind
+  { tsymbol  = 'O'
+  , tname    = "lamp post"
+  , tfreq    = [("lampPostOver_O", 90)]
+  , tcolor   = BrYellow
+  , tcolor2  = Brown
+  , tfeature = []
+  }
+burningBush = TileKind
+  { tsymbol  = 'O'
+  , tname    = "burning bush"
+  , tfreq    = [("lampPostOver_O", 10), ("ambushSet", 3), ("battleSet", 2)]
+  , tcolor   = BrRed
+  , tcolor2  = Red
+  , tfeature = []
+  }
+bush = TileKind
+  { tsymbol  = 'O'
+  , tname    = "bush"
+  , tfreq    = [("ambushSet", 100) ]
+  , tcolor   = Green
+  , tcolor2  = BrBlack
+  , tfeature = [Dark]
+  }
 tree = TileKind
   { tsymbol  = 'O'
   , tname    = "tree"
-  , tfreq    = [("combatSet", 8)]
+  , tfreq    = [("skirmishSet", 14), ("battleSet", 20), ("treeShadeOver_O", 1)]
   , tcolor   = BrGreen
   , tcolor2  = Green
   , tfeature = []
@@ -103,7 +136,7 @@
   , tfreq    = [("legendLit", 100), ("legendDark", 100), ("open door", 1)]
   , tcolor   = Brown
   , tcolor2  = BrBlack
-  , tfeature = [Walkable, Clear, CloseTo "closed door"]
+  , tfeature = [Walkable, Clear, NoItem, NoActor, CloseTo "closed door"]
   }
 stairsUp = TileKind
   { tsymbol  = '<'
@@ -111,7 +144,7 @@
   , tfreq    = []  -- TODO: [("legendLit", 100), ("legendDark", 100)]
   , tcolor   = BrWhite
   , tcolor2  = defFG
-  , tfeature = [Walkable, Clear, Cause $ Effect.Ascend 1]
+  , tfeature = [Walkable, Clear, NoItem, NoActor, Cause $ Effect.Ascend 1]
   }
 stairsDown = TileKind
   { tsymbol  = '>'
@@ -119,23 +152,23 @@
   , tfreq    = []  -- TODO: [("legendLit", 100), ("legendDark", 100)]
   , tcolor   = BrWhite
   , tcolor2  = defFG
-  , tfeature = [Walkable, Clear, Cause $ Effect.Ascend (-1)]
+  , tfeature = [Walkable, Clear, NoItem, NoActor, Cause $ Effect.Ascend (-1)]
   }
 escapeUp = TileKind
   { tsymbol  = '<'
-  , tname    = "exit airlock up"
+  , tname    = "airlock to the shuttle"
   , tfreq    = [("legendLit", 100), ("legendDark", 100)]
   , tcolor   = BrYellow
   , tcolor2  = BrYellow
-  , tfeature = [Walkable, Clear, Cause $ Effect.Escape 1]
+  , tfeature = [Walkable, Clear, NoItem, NoActor, Cause $ Effect.Escape 1]
   }
 escapeDown = TileKind
   { tsymbol  = '>'
-  , tname    = "exit airlock down"
+  , tname    = "airlock to the shuttle"
   , tfreq    = [("legendLit", 100), ("legendDark", 100)]
   , tcolor   = BrYellow
   , tcolor2  = BrYellow
-  , tfeature = [Walkable, Clear, Cause $ Effect.Escape (-1)]
+  , tfeature = [Walkable, Clear, NoItem, NoActor, Cause $ Effect.Escape (-1)]
   }
 liftUp = TileKind
   { tsymbol  = '<'
@@ -143,7 +176,7 @@
   , tfreq    = [("legendLit", 100), ("legendDark", 100)]
   , tcolor   = BrCyan
   , tcolor2  = BrCyan
-  , tfeature = [Walkable, Clear, Cause $ Effect.Ascend 1]
+  , tfeature = [Walkable, Clear, NoItem, NoActor, Cause $ Effect.Ascend 1]
   }
 lift = TileKind
   { tsymbol  = '<'
@@ -151,7 +184,7 @@
   , tfreq    = [("legendLit", 100), ("legendDark", 100)]
   , tcolor   = BrBlue
   , tcolor2  = BrBlue
-  , tfeature = [ Walkable, Clear
+  , tfeature = [ Walkable, Clear, NoItem, NoActor
                , Cause $ Effect.Ascend 1
                , Cause $ Effect.Ascend (-1) ]
   }
@@ -161,7 +194,7 @@
   , tfreq    = [("legendLit", 100), ("legendDark", 100)]
   , tcolor   = BrCyan
   , tcolor2  = BrCyan
-  , tfeature = [Walkable, Clear, Cause $ Effect.Ascend (-1)]
+  , tfeature = [Walkable, Clear, NoItem, NoActor, Cause $ Effect.Ascend (-1)]
   }
 unknown = TileKind
   { tsymbol  = ' '
@@ -175,23 +208,31 @@
   { tsymbol  = '.'
   , tname    = "floor"
   , tfreq    = [ ("floorCorridorLit", 1), ("floorArenaLit", 1)
-               , ("arenaSet", 1), ("noiseSet", 100), ("combatSet", 100) ]
+               , ("arenaSet", 1), ("emptySet", 1), ("noiseSet", 50)
+               , ("battleSet", 1000), ("skirmishSet", 100)
+               , ("ambushSet", 1000) ]
   , tcolor   = BrWhite
   , tcolor2  = defFG
   , tfeature = [Walkable, Clear]
   }
 floorActorLit = floorCorridorLit
-  { tfreq    = [("floorActorLit", 1), ("battleSet", 100)]
-  , tfeature = CanActor : tfeature floorCorridorLit
+  { tfreq    = [("floorActorLit", 1)]
+  , tfeature = OftenActor : tfeature floorCorridorLit
   }
 floorItemLit = floorCorridorLit
   { tfreq    = []
-  , tfeature = CanItem : tfeature floorCorridorLit
+  , tfeature = OftenItem : tfeature floorCorridorLit
   }
 floorActorItemLit = floorItemLit
-  { tfreq    = [("legendLit", 100), ("emptySet", 1)]
-  , tfeature = CanActor : tfeature floorItemLit
+  { tfreq    = [("legendLit", 100)]
+  , tfeature = OftenActor : tfeature floorItemLit
   }
+floorArenaShade = floorActorLit
+  { tname    = "floor"  -- TODO: "shaded ground"
+  , tfreq    = [("treeShadeOver_s", 1)]
+  , tcolor2  = BrBlack
+  , tfeature = Dark : tfeature floorActorLit  -- no OftenItem
+  }
 floorRedLit = floorCorridorLit
   { tname    = "emergency walkway"
   , tfreq    = [("trailLit", 20)]
@@ -214,11 +255,18 @@
 
 
 makeDark :: TileKind -> TileKind
-makeDark k = let textLit :: Text -> Text
-                 textLit t = maybe t (<> "Dark") $ T.stripSuffix "Lit" t
-                 litFreq = map (first textLit) $ tfreq k
-             in k { tfreq    = litFreq
-                  , tfeature = Dark : tfeature k
+makeDark k = let darkText :: Text -> Text
+                 darkText t = maybe t (<> "Dark") $ T.stripSuffix "Lit" t
+                 darkFrequency = map (first darkText) $ tfreq k
+                 darkFeat (OpenTo t) = Just $ OpenTo $ darkText t
+                 darkFeat (CloseTo t) = Just $ CloseTo $ darkText t
+                 darkFeat (ChangeTo t) = Just $ ChangeTo $ darkText t
+                 darkFeat (HideAs t) = Just $ HideAs $ darkText t
+                 darkFeat (RevealAs t) = Just $ RevealAs $ darkText t
+                 darkFeat OftenItem = Nothing
+                 darkFeat feat = Just $ feat
+             in k { tfreq    = darkFrequency
+                  , tfeature = Dark : mapMaybe darkFeat (tfeature k)
                   }
 
 makeDarkColor :: TileKind -> TileKind
diff --git a/GameDefinition/Main.hs b/GameDefinition/Main.hs
--- a/GameDefinition/Main.hs
+++ b/GameDefinition/Main.hs
@@ -1,55 +1,18 @@
-{-# OPTIONS_GHC -fno-warn-orphans #-}
 -- Copyright (c) 2008--2011 Andres Loeh, 2010--2014 Mikolaj Konarski
 -- This file is a part of the computer game Allure of the Stars
 -- and is released under the terms of the GNU Affero General Public License.
 -- For license and copyright information, see the file LICENSE.
 --
--- | The main source code file of Allure of the Stars. Here the knot of engine
--- code pieces and the Allure-specific content defintions is tied,
--- resulting in an executable game.
+-- | The main source code file of Allure of the Stars.
 module Main ( main ) where
 
-import qualified Content.ActorKind
-import qualified Content.CaveKind
-import qualified Content.FactionKind
-import qualified Content.ItemKind
-import qualified Content.ModeKind
-import qualified Content.PlaceKind
-import qualified Content.RuleKind
-import qualified Content.TileKind
-import Game.LambdaHack.Client
-import Game.LambdaHack.Client.Action.ActionType
-import Game.LambdaHack.Common.Action (MonadAtomic (..))
-import Game.LambdaHack.Common.AtomicCmd
-import Game.LambdaHack.Common.AtomicSem
-import qualified Game.LambdaHack.Common.Kind as Kind
-import Game.LambdaHack.Server
-import Game.LambdaHack.Server.Action.ActionType
-import Game.LambdaHack.Server.AtomicSemSer
-
--- | The game-state semantics of atomic game commands
--- as computed on the server.
-instance MonadAtomic ActionSer where
-  execAtomic = atomicSendSem
+import System.Environment (getArgs)
 
--- | The game-state semantics of atomic game commands
--- as computed on clients. Special effects (@SfxAtomic@) don't modify state.
-instance MonadAtomic (ActionCli c d) where
-  execAtomic (CmdAtomic cmd) = cmdAtomicSem cmd
-  execAtomic (SfxAtomic _) = return ()
+import TieKnot
 
--- | Tie the LambdaHack engine clients and server code
--- with the Allure-specific content defintions and run the game.
+-- | Tie the LambdaHack engine client, server and frontend code
+-- with the game-specific content definitions, and run the game.
 main :: IO ()
-main =
-  let copsSlow = Kind.COps
-        { coactor   = Kind.createOps Content.ActorKind.cdefs
-        , cocave    = Kind.createOps Content.CaveKind.cdefs
-        , cofaction = Kind.createOps Content.FactionKind.cdefs
-        , coitem    = Kind.createOps Content.ItemKind.cdefs
-        , comode    = Kind.createOps Content.ModeKind.cdefs
-        , coplace   = Kind.createOps Content.PlaceKind.cdefs
-        , corule    = Kind.createOps Content.RuleKind.cdefs
-        , cotile    = Kind.createOps Content.TileKind.cdefs
-        }
-  in mainSer copsSlow executorSer $ exeFrontend executorCli executorCli
+main = do
+  args <- getArgs
+  tieKnot args
diff --git a/GameDefinition/PLAYING.md b/GameDefinition/PLAYING.md
--- a/GameDefinition/PLAYING.md
+++ b/GameDefinition/PLAYING.md
@@ -1,35 +1,64 @@
 Playing Allure of the Stars
 ===========================
 
-Playing Allure of the Stars involves walking around an abandoned
-Solar System space-ship, alone or in a party of trusted crew-members,
-setting up ambushes, hiding in shadow, covering tracks,
-breaking through to deeper spaceship decks, bumping into
-hostile intruders, doors and walls, gathering technical resources
-and making creative use of them. The bloodthirsty alien monsters
-and their horrible minions do the same, intelligence allowing,
-while tirelessly chasing the stranded heroes by smell and sight.
+Your party of trusted crew-members is set to test their fortune
+by exploring an abandoned Solar System passenger cruiser.
+You are going to set up ambushes for it's feral denizens, hide in shadows,
+cover tracks, bump into unspeakable horrors, hidden mechanisms
+and astounding technical treasures and make creative use of it all.
+The mysterious inhabitants of the gigantic spaceship are bound
+to welcome intruders with bloody surprises of their own.
+As soon as you turn your back in fear, expect to be chased tirelessly
+throughout all the decks, by sight, sound and smell.
 
 Once the few basic command keys and on-screen symbols are learned,
 mastery and enjoyment of the game is the matter of tactical skill
 and literary imagination. To be honest, a lot of imagination is required
 at this stage, but the game is already playable and winnable.
-The game also features experimental multiplayer cooperative and competitive
-modes, but they are troublesome to play with the shared-screen
-and shared-keyboard interface available at this time.
-Contributions welcome.
+Contributions are welcome.
 
 
-Terrain
--------
+Heroes
+------
 
 The heroes are marked on the map with symbols `@` and `1` through `9`.
-Their goal is to explore an old, gigantic, once luxurious space liner,
+Their goal is to explore an old, warped, once luxurious, space liner,
 battle the horrors within, gather as much gold and precious gems
-as possible, and escape to tell the tale. The spaceship, in the campaign
-game mode, consists of many levels covered with varying terrain
-of the following basic kinds.
+as possible, and escape to tell the tale.
 
+The currently chosen party leader is highlighted on the screen
+and his attributes are displayed at the bottommost status line,
+which in its most complex form may look as follows.
+
+    *@12 Adventurer  4d1+5% Calm: 20/60 HP: 33/50 Target: basilisk  [**___]
+
+The line starts with the list of party members (unless only one member
+resides on the currently displayed level) and the shortened name of the team.
+Then comes the damage of the leader's weapon (but regardless of the figures,
+each attack inflicts at least 1 damage), then his current and maximum
+Calm (composure, focus, attentiveness), then his current and maximum
+HP (hit points, health). At the end, the personal target of the leader
+is described, in this case a basilisk monster, with hit points drawn as a bar.
+
+The other status line describes the current location in relation to the party.
+
+    5  Lofty hall   [33% seen] Cursor: exact spot (71,12)  p15 l10
+
+First comes the depth of the current level and its name.
+Then the percentage of its explorable tiles already seen by the heroes.
+The 'cursor' is the common target of the whole party,
+directly manipulated with movement keys in the targeting mode.
+At the end comes the length of the shortest path from the leader
+to the cursor position and the straight-line distance between the two points.
+
+
+Spaceship
+---------
+
+The spaceship of the campaign mode game consists of decks on many levels
+and each deck consists of a large number of tiles. The basic tile kinds
+are as follows.
+
                terrain type                       on-screen symbol
                floor                              .
                wall                               #
@@ -40,77 +69,90 @@
                open door                          '
                closed door                        +
 
-The game world is persistent, i.e., every time the player visits a level
-during a single game, the level layout is the same.
+The game world is persistent, i.e., every time the player visits a deck
+during a single game, its layout is the same.
 
 
-Keys
-----
+Commands
+--------
 
-You move throughout the level using the numerical keypad or
-the vi text editor keys (also known as "Rogue-like keys").
+You move throughout the level using the numerical keypad (left diagram)
+or its compact laptop replacement (middle) or Vi text editor keys
+(right, also known as "Rogue-like keys", which have to be enabled
+in config.ui.ini).
 
-                7 8 9          y k u
-                 \|/            \|/
-                4-5-6          h-.-l
-                 /|\            /|\
-                1 2 3          b j n
+                7 8 9          7 8 9          y k u
+                 \|/            \|/            \|/
+                4-5-6          u-i-o          h-.-l
+                 /|\            /|\            /|\
+                1 2 3          j k l          b j n
 
+In targeting mode the keys above move the targeting cursor. In normal mode,
 `SHIFT` (or `CTRL`) and a movement key make the current party leader
 (and currently selected party members, if any) run in the indicated
 direction, until anything of interest is spotted.
-The `5` and `.` keys consume a turn and make you brace for combat,
-which confers a chance to block blows for the remainder of the turn.
-In targeting mode the same keys move the targeting cursor.
+The '5', 'i' and '.' keys consume a turn and make you brace for combat,
+which reduces any damage taken for a turn and makes it impossible
+for foes to displace you. You displace enemies or friends by bumping
+into them with SHIFT (or CTRL).
 
-Melee, searching for secret doors and opening closed doors
+Melee, searching for secret doors, looting and opening closed doors
 can be done by bumping into a monster, a wall and a door, respectively.
-Few commands other than movement are necessary for casual play.
-Some are provided only as building blocks for more complex convenience
-commands, e.g., the autoexplore command (key `X`) could be defined
-by the player as a macro using `BACKSPACE`, `CTRL-?`, `CTRL-;` and `P`.
+Few commands other than movement, 'g'etting an item from the floor,
+'a'pplying an item and 'f'linging an item are necessary for casual play.
+Some are provided only as specialized versions of more general commands
+or as building blocks for more complex convenience commands,
+e.g., the autoexplore command (key `X`) could be defined
+by the player as a macro using `BACKSPACE`, `CTRL-?`, `;` and `V`.
 
-Below are the remaining keys for movement and terrain alteration.
+Below are the remaining keys for terrain exploration and alteration.
 
                 keys           command
                 <              ascend a level
                 CTRL-<         ascend 10 levels
                 >              descend a level
                 CTRL->         descend 10 levels
-                CTRL-;         make one step towards the target
-                ;              go to target for 100 steps
+                ;              make one step towards the target
+                :              go to target for 100 steps
+                CTRL-:         go to target for 10 steps
                 x              explore the closest unknown spot
                 X              autoexplore 100 times
+                CTRL-X         autoexplore 10 times
                 R              rest (wait 100 times)
+                CTRL-R         rest (wait 10 times)
                 c              close door
 
-Inventory and items-related keys are as follows. The `a` and `f` commands
-are generalized versions the other item manipulation commands, offering
-a wider default item choice.
-
+Item-use related keys are as follows.
 
                 keys           command
-                I              display inventory
-                g and ,        get an object
-                d              drop an object
-                q              quaff drink
-                r              read tablet
-                a              apply consumable
-                t              throw projectile
-                z              zap mechanism
-                f              fling missile
+                E              describe equipment of the leader
+                P              describe backpack inventory of the leader
+                S              describe the shared party stash
+                G              describe items on the ground
+                A              describe all owned items
+                g and ,        get an item
+                d              drop an item
+                e              equip an item
+                p              pack an item into inventory backpack
+                s              stash and share an item
+                a              activate applicable item
+                q              quaff potion
+                r              read scroll
+                f              fling projectable item
+                t              throw missile
+                z              zap wand
 
 To make a ranged attack, as in the last few commands above,
 you need to set your target first (however, initial target is set
 automatically as soon as a monster comes into view). Once in targeting mode,
 you can move the targeting cursor with arrow keys and switch focus
-among enemies with `*` (or among friends and enemies, depending
+among enemies with `*` (or among friends, projectiles and enemies, depending
 on targeting mode set by `/`). The details of the shared cursor position
 and of the personal target are described at the bottom of the screen.
 All targeting keys are listed below.
 
                 keys           command
-                *              target enemy
+                KEYPAD_* and \ target enemy
                 /              cycle targeting mode
                 +              swerve targeting line
                 -              unswerve targeting line
@@ -120,88 +162,100 @@
                 CTRL-{         target the closest stairs up
                 CTRL-}         target the closest stairs down
 
+Here are the commands for automating the actions of one or more members
+of the team.
+
+                keys           command
+                =              select (or deselect) a party member
+                _              deselect (or select) all on the level
+                v              voice again the recorded commands
+                V              voice the recorded commands 100 times
+                CTRL-v         voice the recorded commands 1000 times
+                CTRL-V         voice the recorded commands 10 times
+                '              start recording commands
+                CTRL-A         automate faction (ESC to retake control)
+
 Assorted remaining keys and commands follow.
 
                 keys           command
                 ?              display help
                 D              display player diary
                 T              mark suspect terrain
-                V              mark visible area
-                S              mark smell
+                Z              mark visible zone
+                C              mark smell clues
                 TAB            cycle among party members on the level
-                SHIFT-TAB      cycle among party members in the dungeon
-                =              select (or deselect) a party member
-                _              deselect (or select) all on the level
-                p              play back last keys
-                P              play back last keys 100 times
-                CTRL-p         play back last keys 1000 times
-                '              start recording a macro
+                SHIFT-TAB      cycle among all party members
                 SPACE          clear messages
-                ESC            cancel action
+                ESC            cancel action, open Main Menu
                 RET            accept choice
-                0--9           pick a new hero leader anywhere in the dungeon
+                0--6           pick a new hero leader anywhere in the spaceship
 
 Commands for saving and exiting the current game, starting a new game, etc.,
 are listed in the Main Menu, brought up by the `ESC` key.
-All but the campaign and skirmish game modes are experimental
-and feature multiple human or computer players (allied or not).
 Game difficulty setting affects hitpoints at birth for any actors
-of any UI-using faction.
+of any UI-using faction. For a person new to roguelikes, the Duel game mode
+offers a gentle introduction. The subsequent game modes gradually introduce
+squad combat, stealth, asymmetric battles and more game elements.
 
                 keys           command
                 CTRL-x         save and exit
-                CTRL-r         new campaign game
-                CTRL-k         new skirmish game
-                CTRL-v         new PvP game
-                CTRL-o         new Coop game
-                CTRL-e         new defense game
+                CTRL-u         new Duel game
+                CTRL-k         new Skirmish game
+                CTRL-m         new Ambush game
+                CTRL-b         new Battle game
+                CTRL-a         new Campaign game
                 CTRL-d         cycle next game difficulty
 
 There are also some debug, testing and cheat options and game modes
 that can be specified on the command line when starting the game server.
-Use at your own peril! :) Of these, you may find the screensaver modes
+Use at your own peril! :) Of these, you may find the screensaver game modes
 the least spoilery and the most fun, e.g.:
 
-    Allure --newGame --noMore --maxFps 45 --savePrefix test --gameMode testCampaign --difficulty 1
+    Allure --savePrefix test --newGame --noMore --maxFps 60 --automateAll --gameMode campaign --difficulty 1
 
+The `--automateAll` option strictly corresponds to the `CTRL-A` command,
+but most of the debug options have no corresponding commands.
 
+
 Monsters
 --------
 
-Heroes are not alone in the spaceship. Monsters roam the dark halls
-and crawl from damp air-ducts day and night. While heroes pay attention
-to all other party members and take care to move one at a time,
-monsters don't care about each other and all move at once,
-sometimes brutally colliding by accident.
+Heroes are not alone in the spaceship. Monstrosities, natural
+and out of this world, roam the dark halls and crawl from damp air-ducts
+day and night. While heroes pay attention to all other party members
+and take care to move one at a time, monsters don't care about each other
+and all move at once, sometimes brutally colliding by accident.
 
 When the hero bumps into a monster or a monster attacks the hero,
-melee combat occurs. The best weapon carried by each opponent
-is taken into account for calculating bonus damage. The total damage
-the current hero can potentially inflict is displayed at the bottom
-of the screen. The total damage potential of a monster may change
-as it finds and picks up new weapons. Heroes and monsters running into
-one another (with the `SHIFT` key) do not inflict damage, but change places.
+melee combat occurs. The best equipped weapon or the best fighting organ
+of each opponent is taken into account for calculating damage.
+The damage the current hero can potentially inflict is displayed
+at the bottom of the screen, but the actual damage depends also
+on the monster's armor. Heroes and monsters running into one another
+(with the `SHIFT` key) do not inflict damage, but change places.
 This gives the opponent a free blow, but can improve the tactical situation
 or aid escape.
 
-Throwing weapons at targets wounds them, consuming the weapon in the process.
-You may throw any object in your possession (press `?` to choose
-an object and press it again for a non-standard choice) or on the floor
-(press `-`). Only objects of a few kinds inflict any damage.
+Slinging a missile at a target wounds it, consuming the weapon in the process.
+You may propel any item in your equipment, inventory and on the ground
+(press `?` to choose an item and press it again for a non-standard choice).
+Only items of a few kinds inflict any damage, but some have other effects.
 Whenever the monster's or hero's hit points reach zero, the combatant dies.
-When the last hero dies, the game ends.
+When the last hero dies, the game ends in defeat.
 
 
 On Winning and Dying
 --------------------
 
-You win the game if you escape the spaceship alive (or eliminate
-all opposition, in some game modes). Your score is
-the sum of all gold you've plundered plus 100 gold grains for each gem.
-Only the loot in possession of the party members on the current level
-counts (the rest of the party is considered MIA).
+You win the game if you escape the spaceship alive or, in game modes with
+no exit opportunity, if you eliminate all opposition. In the former case,
+your score is based on the gold and precious gems you've plundered,
+plus a bonus based on the number of heroes you lost. In the latter case,
+your score is based on the number of turns you spent overcoming your foes
+and, as a bonus, the number of enemies you've subdued.
 
-If all heroes die, your score is halved and only the treasure carried
-by the last standing hero counts. You are free to start again
-from a different entrance to the spaceship, but all your previous wealth
-is gone and fresh, undaunted enemies bar your way.
+If all your heroes fall, you are awarded a score for your valiant deeds,
+but no winning bonus. When, invariably, a new overconfident party
+of adventurers storms the spaceship, they start from a new entrance,
+with no experience and no equipment, and new, undaunted enemies
+bar their way.
diff --git a/GameDefinition/TieKnot.hs b/GameDefinition/TieKnot.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/TieKnot.hs
@@ -0,0 +1,43 @@
+-- Copyright (c) 2008--2011 Andres Loeh, 2010--2014 Mikolaj Konarski
+-- This file is a part of the computer game Allure of the Stars
+-- and is released under the terms of the GNU Affero General Public License.
+-- For license and copyright information, see the file LICENSE.
+--
+-- | Here the knot of engine code pieces and the game-specific
+-- content definitions is tied, resulting in an executable game.
+module TieKnot ( tieKnot ) where
+
+import qualified Client.UI.Content.KeyKind as Content.KeyKind
+import qualified Content.CaveKind
+import qualified Content.FactionKind
+import qualified Content.ItemKind
+import qualified Content.ModeKind
+import qualified Content.PlaceKind
+import qualified Content.RuleKind
+import qualified Content.TileKind
+import Game.LambdaHack.Client (exeFrontend)
+import qualified Game.LambdaHack.Common.Kind as Kind
+import Game.LambdaHack.SampleImplementation.SampleMonadClient (executorCli)
+import Game.LambdaHack.SampleImplementation.SampleMonadServer (executorSer)
+import Game.LambdaHack.Server (mainSer)
+
+-- | Tie the LambdaHack engine client, server and frontend code
+-- with the game-specific content definitions, and run the game.
+tieKnot :: [String] -> IO ()
+tieKnot args =
+  let -- Common content operations, created from content definitions.
+      copsServer = Kind.COps
+        { cocave    = Kind.createOps Content.CaveKind.cdefs
+        , cofaction = Kind.createOps Content.FactionKind.cdefs
+        , coitem    = Kind.createOps Content.ItemKind.cdefs
+        , comode    = Kind.createOps Content.ModeKind.cdefs
+        , coplace   = Kind.createOps Content.PlaceKind.cdefs
+        , corule    = Kind.createOps Content.RuleKind.cdefs
+        , cotile    = Kind.createOps Content.TileKind.cdefs
+        }
+      -- Client content operations.
+      copsClient = Content.KeyKind.standardKeys
+      -- A single frontend is currently started by the server,
+      -- instead of each client starting it's own.
+      startupFrontend = exeFrontend executorCli executorCli copsClient
+  in mainSer args copsServer executorSer startupFrontend
diff --git a/GameDefinition/config.ui.default b/GameDefinition/config.ui.default
--- a/GameDefinition/config.ui.default
+++ b/GameDefinition/config.ui.default
@@ -10,19 +10,21 @@
 ; ; Warning: options are case-sensitive and only ';' for comments is permitted.
 
 ; [extra_commands]
-; ; Handy shorthands with Vi keys:
-; Macro_1 = ("comma", (CmdItem, Macro "" ["g"]))
-; Macro_2 = ("period", (CmdMove, Macro "" ["KP_Begin"]))
+; ; A handy shorthand with Vi keys:
+; Macro_1 = ("comma", ([CmdItem], Macro "" ["g"]))
 
 ; [hero_names]
-; HeroName_0 = Haskell Alvin
-; HeroName_1 = Alonzo Barkley
-; HeroName_2 = Ines Galenti
-; HeroName_3 = Ernst Abraham
-; HeroName_4 = Samuel Saunders
-; HeroName_5 = Roger Robin
+; HeroName_0 = ("Haskell Alvin", "he")
+; HeroName_1 = ("Alonzo Barkley", "he")
+; HeroName_2 = ("Ines Galenti", "she")
+; HeroName_3 = ("Ernst Abraham", "he")
+; HeroName_4 = ("Samuel Saunders", "he")
+; HeroName_5 = ("Roger Robin", "he")
+; HeroName_6 = ("Christopher Flatt", "he")
 
 ; [ui]
+; movementViKeys_hjklyubn = False
+; movementLaptopKeys_uk8o79jl = True
 ; font = "Terminus,Monospace normal normal normal normal 12"
 ; historyMax = 5000
 ; maxFps = 15
diff --git a/GameDefinition/scores b/GameDefinition/scores
Binary files a/GameDefinition/scores and b/GameDefinition/scores differ
diff --git a/GameDefinition/screenshot.png b/GameDefinition/screenshot.png
new file mode 100644
Binary files /dev/null and b/GameDefinition/screenshot.png differ
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -15,229 +15,245 @@
 
 
 xcplay:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer
-
-xcpeekCampaign:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix peekCampaign --dumpInitRngs --gameMode peekCampaign
-
-xcpeekSkirmish:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix peekSkirmish --dumpInitRngs --gameMode peekSkirmish
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --dumpInitRngs
 
 xcfrontendCampaign:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testCampaign --difficulty 1
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode campaign --difficulty 1
 
 xcfrontendSkirmish:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testSkirmish
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode skirmish
 
-xcfrontendBattle:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testBattle --difficulty 1
+xcfrontendAmbush:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode ambush
 
-xcfrontendPvP:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testPvP --fovMode Shadow
+xcfrontendBattle:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode battle --difficulty 1
 
-xcfrontendCoop:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testCoop --difficulty 1 --fovMode Permissive
+xcfrontendSafari:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode safari --difficulty 1
 
 xcfrontendDefense:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testDefense --difficulty 9
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode defense --difficulty 9
 
 xcbenchCampaign:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendNo --benchmark --stopAfter 60 --gameMode testCampaign --difficulty 1 --setDungeonRng 42 --setMainRng 42
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfter 60 --automateAll --gameMode campaign --difficulty 1 --setDungeonRng 42 --setMainRng 42
 
 xcbenchBattle:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendNo --benchmark --stopAfter 60 --gameMode testBattle --difficulty 1 --setDungeonRng 42 --setMainRng 42
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfter 60 --automateAll --gameMode battle --difficulty 1 --setDungeonRng 42 --setMainRng 42
 
 xcbenchFrontendCampaign:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 100000 --savePrefix test --benchmark --stopAfter 60 --gameMode testCampaign --difficulty 1 --setDungeonRng 42 --setMainRng 42
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 100000 --benchmark --stopAfter 60 --automateAll --gameMode campaign --difficulty 1 --setDungeonRng 42 --setMainRng 42
 
 xcbenchFrontendBattle:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 100000 --savePrefix test --benchmark --stopAfter 60 --gameMode testBattle --difficulty 1 --setDungeonRng 42 --setMainRng 42
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 100000 --benchmark --stopAfter 60 --automateAll --gameMode battle --difficulty 1 --setDungeonRng 42 --setMainRng 42
 
-xcbenchNo: xcbenchCampaign xcbenchBattle
+xcbenchNull: xcbenchCampaign xcbenchBattle
 
 xcbench: xcbenchCampaign xcbenchFrontendCampaign xcbenchBattle xcbenchFrontendBattle
 
-xctest-travis: xctest-short xctest-medium xcbenchNo
 
-xctest-travis-long: xctest-short xctest-long xcbenchNo
+xctest-travis-short: xctest-short xcbenchNull
 
+xctest-travis: xctest-short xctest-medium xcbenchNull
+
+xctest-travis-long: xctest-short xctest-long xcbenchNull
+
 xctest: xctest-short xctest-medium xctest-long
 
 xctest-short: xctest-short-new xctest-short-load
 
-xctest-medium: xctestCampaign-medium xctestSkirmish-medium xctestBattle-medium xctestPvP-medium xctestCoop-medium xctestDefense-medium
+xctest-medium: xctestCampaign-medium xctestSkirmish-medium xctestAmbush-medium xctestBattle-medium xctestSafari-medium xctestPvP-medium xctestCoop-medium xctestDefense-medium
 
-xctest-long: xctestCampaign-long xctestCoop-long xctestDefense-long
+xctest-long: xctestCampaign-long xctestSkirmish-long xctestAmbush-long xctestBattle-long xctestSafari-long xctestPvP-long xctestCoop-long xctestDefense-long
 
 xctestCampaign-long:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testCampaign --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 400 --dumpInitRngs --automateAll --gameMode campaign --difficulty 1 > /tmp/stdtest.log
 
 xctestCampaign-medium:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testCampaign --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 250 --dumpInitRngs --automateAll --gameMode campaign --difficulty 1 > /tmp/stdtest.log
 
 xctestSkirmish-long:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testSkirmish > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode skirmish > /tmp/stdtest.log
 
 xctestSkirmish-medium:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testSkirmish > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode skirmish > /tmp/stdtest.log
 
+xctestAmbush-long:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode ambush > /tmp/stdtest.log
+
+xctestAmbush-medium:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode ambush > /tmp/stdtest.log
+
 xctestBattle-long:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testBattle --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 400 --dumpInitRngs --automateAll --gameMode battle --difficulty 1 > /tmp/stdtest.log
 
 xctestBattle-medium:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testBattle --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 250 --dumpInitRngs --automateAll --gameMode battle --difficulty 1 > /tmp/stdtest.log
 
+xctestSafari-long:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 400 --dumpInitRngs --automateAll --gameMode safari --difficulty 1 > /tmp/stdtest.log
+
+xctestSafari-medium:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 250 --dumpInitRngs --automateAll --gameMode safari --difficulty 1 > /tmp/stdtest.log
+
 xctestPvP-long:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testPvP > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode PvP > /tmp/stdtest.log
 
 xctestPvP-medium:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testPvP > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode PvP > /tmp/stdtest.log
 
 xctestCoop-long:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testCoop --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode Coop --difficulty 1 > /tmp/stdtest.log
 
 xctestCoop-medium:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testCoop --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 250 --dumpInitRngs --automateAll --gameMode Coop --difficulty 1 > /tmp/stdtest.log
 
 xctestDefense-long:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testDefense --difficulty 9 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 400 --dumpInitRngs --automateAll --gameMode defense --difficulty 9 > /tmp/stdtest.log
 
 xctestDefense-medium:
-	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testDefense --difficulty 9 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 250 --dumpInitRngs --automateAll --gameMode defense --difficulty 9 > /tmp/stdtest.log
 
 xctest-short-new:
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --savePrefix campaign --dumpInitRngs --gameMode campaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --savePrefix skirmish --dumpInitRngs --gameMode skirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --savePrefix battle --dumpInitRngs --gameMode battle --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --savePrefix PvP --dumpInitRngs --gameMode PvP --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --savePrefix Coop --dumpInitRngs --gameMode Coop --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --savePrefix defense --dumpInitRngs --gameMode defense --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --savePrefix peekCampaign --dumpInitRngs --gameMode peekCampaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --savePrefix peekSkirmish --dumpInitRngs --gameMode peekSkirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --savePrefix campaign --dumpInitRngs --automateAll --gameMode campaign --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --savePrefix skirmish --dumpInitRngs --automateAll --gameMode skirmish --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --savePrefix ambush --dumpInitRngs --automateAll --gameMode ambush --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --savePrefix battle --dumpInitRngs --automateAll --gameMode battle --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --savePrefix safari --dumpInitRngs --automateAll --gameMode safari --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --savePrefix PvP --dumpInitRngs --automateAll --gameMode PvP --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --savePrefix Coop --dumpInitRngs --automateAll --gameMode Coop --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --savePrefix defense --dumpInitRngs --automateAll --gameMode defense --frontendStd --stopAfter 2 > /tmp/stdtest.log
 
 xctest-short-load:
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix campaign --dumpInitRngs --gameMode campaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix skirmish --dumpInitRngs --gameMode skirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix battle --dumpInitRngs --gameMode battle --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix PvP --dumpInitRngs --gameMode PvP --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix Coop --dumpInitRngs --gameMode Coop --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix defense --dumpInitRngs --gameMode defense --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix peekCampaign --dumpInitRngs --gameMode peekCampaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix peekSkirmish --dumpInitRngs --gameMode peekSkirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --noMore --savePrefix campaign --dumpInitRngs --automateAll --gameMode campaign --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --noMore --savePrefix skirmish --dumpInitRngs --automateAll --gameMode skirmish --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --noMore --savePrefix ambush --dumpInitRngs --automateAll --gameMode ambush --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --noMore --savePrefix battle --dumpInitRngs --automateAll --gameMode battle --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --noMore --savePrefix safari --dumpInitRngs --automateAll --gameMode safari --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --noMore --savePrefix PvP --dumpInitRngs --automateAll --gameMode PvP --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --noMore --savePrefix Coop --dumpInitRngs --automateAll --gameMode Coop --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --noMore --savePrefix defense --dumpInitRngs --automateAll --gameMode defense --frontendStd --stopAfter 2 > /tmp/stdtest.log
 
 
 play:
-	dist/build/Allure/Allure --dbgMsgSer
-
-peekCampaign:
-	dist/build/Allure/Allure --dbgMsgSer --savePrefix peekCampaign --dumpInitRngs --gameMode peekCampaign
-
-peekSkirmish:
-	dist/build/Allure/Allure --dbgMsgSer --savePrefix peekSkirmish --dumpInitRngs --gameMode peekSkirmish
+	dist/build/Allure/Allure --dbgMsgSer --dumpInitRngs
 
 frontendCampaign:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testCampaign --difficulty 1
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode campaign --difficulty 1
 
 frontendSkirmish:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testSkirmish
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode skirmish
 
-frontendBattle:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testBattle --difficulty 1
+frontendAmbush:
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode ambush
 
-frontendPvP:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testPvP --fovMode Shadow
+frontendBattle:
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode battle --difficulty 1
 
-frontendCoop:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testCoop --difficulty 1 --fovMode Permissive
+frontendSafari:
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode safari --difficulty 1
 
 frontendDefense:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 180 --savePrefix test --dumpInitRngs --gameMode testDefense --difficulty 9
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 60 --dumpInitRngs --automateAll --gameMode defense --difficulty 9
 
 benchCampaign:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendNo --benchmark --stopAfter 60 --gameMode testCampaign --difficulty 1 --setDungeonRng 42 --setMainRng 42
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfter 60 --automateAll --gameMode campaign --difficulty 1 --setDungeonRng 42 --setMainRng 42
 
 benchBattle:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendNo --benchmark --stopAfter 60 --gameMode testBattle --difficulty 1 --setDungeonRng 42 --setMainRng 42
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfter 60 --automateAll --gameMode battle --difficulty 1 --setDungeonRng 42 --setMainRng 42
 
 benchFrontendCampaign:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 100000 --savePrefix test --benchmark --stopAfter 60 --gameMode testCampaign --difficulty 1 --setDungeonRng 42 --setMainRng 42
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 100000 --benchmark --stopAfter 60 --automateAll --gameMode campaign --difficulty 1 --setDungeonRng 42 --setMainRng 42
 
 benchFrontendBattle:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 100000 --savePrefix test --benchmark --stopAfter 60 --gameMode testBattle --difficulty 1 --setDungeonRng 42 --setMainRng 42
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 100000 --benchmark --stopAfter 60 --automateAll --gameMode battle --difficulty 1 --setDungeonRng 42 --setMainRng 42
 
-benchNo: benchCampaign benchBattle
+benchNull: benchCampaign benchBattle
 
 bench: benchCampaign benchFrontendCampaign benchBattle benchFrontendBattle
 
 
-test-travis: test-short test-medium benchNo
+test-travis-short: test-short benchNull
 
-test-travis-long: test-short test-long benchNo
+test-travis: test-short test-medium benchNull
 
+test-travis-long: test-short test-long benchNull
+
 test: test-short test-medium test-long
 
 test-short: test-short-new test-short-load
 
-test-medium: testCampaign-medium testSkirmish-medium testBattle-medium testPvP-medium testCoop-medium testDefense-medium
+test-medium: testCampaign-medium testSkirmish-medium testAmbush-medium testBattle-medium testSafari-medium testPvP-medium testCoop-medium testDefense-medium
 
-test-long: testCampaign-long testCoop-long testDefense-long
+test-long: testCampaign-long testSkirmish-long testAmbush-long testBattle-long testSafari-long testPvP-long testCoop-long testDefense-long
 
 testCampaign-long:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testCampaign --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 400 --dumpInitRngs --automateAll --gameMode campaign --difficulty 1 > /tmp/stdtest.log
 
 testCampaign-medium:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testCampaign --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 250 --dumpInitRngs --automateAll --gameMode campaign --difficulty 1 > /tmp/stdtest.log
 
 testSkirmish-long:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testSkirmish > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode skirmish > /tmp/stdtest.log
 
 testSkirmish-medium:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testSkirmish > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode skirmish > /tmp/stdtest.log
 
+testAmbush-long:
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode ambush > /tmp/stdtest.log
+
+testAmbush-medium:
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode ambush > /tmp/stdtest.log
+
 testBattle-long:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testBattle --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 400 --dumpInitRngs --automateAll --gameMode battle --difficulty 1 > /tmp/stdtest.log
 
 testBattle-medium:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testBattle --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 250 --dumpInitRngs --automateAll --gameMode battle --difficulty 1 > /tmp/stdtest.log
 
+testSafari-long:
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 400 --dumpInitRngs --automateAll --gameMode safari --difficulty 1 > /tmp/stdtest.log
+
+testSafari-medium:
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 250 --dumpInitRngs --automateAll --gameMode safari --difficulty 1 > /tmp/stdtest.log
+
 testPvP-long:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testPvP > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode PvP > /tmp/stdtest.log
 
 testPvP-medium:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testPvP > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode PvP > /tmp/stdtest.log
 
 testCoop-long:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testCoop --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 30 --dumpInitRngs --automateAll --gameMode Coop --difficulty 1 > /tmp/stdtest.log
 
 testCoop-medium:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testCoop --difficulty 1 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 250 --dumpInitRngs --automateAll --gameMode Coop --difficulty 1 > /tmp/stdtest.log
 
 testDefense-long:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 580 --dumpInitRngs --gameMode testDefense --difficulty 9 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 400 --dumpInitRngs --automateAll --gameMode defense --difficulty 9 > /tmp/stdtest.log
 
 testDefense-medium:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --frontendStd --benchmark --stopAfter 280 --dumpInitRngs --gameMode testDefense --difficulty 9 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendStd --benchmark --stopAfter 250 --dumpInitRngs --automateAll --gameMode defense --difficulty 9 > /tmp/stdtest.log
 
 test-short-new:
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix campaign --dumpInitRngs --gameMode campaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix skirmish --dumpInitRngs --gameMode skirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix battle --dumpInitRngs --gameMode battle --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix PvP --dumpInitRngs --gameMode PvP --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix Coop --dumpInitRngs --gameMode Coop --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix defense --dumpInitRngs --gameMode defense --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix peekCampaign --dumpInitRngs --gameMode peekCampaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix peekSkirmish --dumpInitRngs --gameMode peekSkirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --savePrefix campaign --dumpInitRngs --automateAll --gameMode campaign --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --savePrefix skirmish --dumpInitRngs --automateAll --gameMode skirmish --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --savePrefix ambush --dumpInitRngs --automateAll --gameMode ambush --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --savePrefix battle --dumpInitRngs --automateAll --gameMode battle --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --savePrefix safari --dumpInitRngs --automateAll --gameMode safari --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --savePrefix PvP --dumpInitRngs --automateAll --gameMode PvP --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --savePrefix Coop --dumpInitRngs --automateAll --gameMode Coop --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --savePrefix defense --dumpInitRngs --automateAll --gameMode defense --frontendStd --stopAfter 2 > /tmp/stdtest.log
 
 test-short-load:
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix campaign --dumpInitRngs --gameMode campaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix skirmish --dumpInitRngs --gameMode skirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix battle --dumpInitRngs --gameMode battle --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix PvP --dumpInitRngs --gameMode PvP --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix Coop --dumpInitRngs --gameMode Coop --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix defense --dumpInitRngs --gameMode defense --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix peekCampaign --dumpInitRngs --gameMode peekCampaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix peekSkirmish --dumpInitRngs --gameMode peekSkirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
-
+	dist/build/Allure/Allure --dbgMsgSer --noMore --savePrefix campaign --dumpInitRngs --automateAll --gameMode campaign --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --noMore --savePrefix skirmish --dumpInitRngs --automateAll --gameMode skirmish --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --noMore --savePrefix ambush --dumpInitRngs --automateAll --gameMode ambush --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --noMore --savePrefix battle --dumpInitRngs --automateAll --gameMode battle --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --noMore --savePrefix safari --dumpInitRngs --automateAll --gameMode safari --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --noMore --savePrefix PvP --dumpInitRngs --automateAll --gameMode PvP --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --noMore --savePrefix Coop --dumpInitRngs --automateAll --gameMode Coop --frontendStd --stopAfter 2 > /tmp/stdtest.log
+	dist/build/Allure/Allure --dbgMsgSer --noMore --savePrefix defense --dumpInitRngs --automateAll --gameMode defense --frontendStd --stopAfter 2 > /tmp/stdtest.log
 
 
 # The rest of the makefile is unmaintained at the moment.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,9 +1,14 @@
-Allure of the Stars [![Build Status](https://secure.travis-ci.org/Mikolaj/Allure.png)](http://travis-ci.org/Mikolaj/Allure)[![Build Status](https://drone.io/github.com/Mikolaj/Allure/status.png)](https://drone.io/github.com/Mikolaj/Allure/latest)
+Allure of the Stars [![Build Status](https://secure.travis-ci.org/AllureOfTheStars/Allure.png)](http://travis-ci.org/AllureOfTheStars/Allure)[![Build Status](https://drone.io/github.com/AllureOfTheStars/Allure/status.png)](https://drone.io/github.com/AllureOfTheStars/Allure/latest)
 ===================
 
-This is an alpha release of Allure of the Stars,
+This is an alpha release of [Allure of the Stars] [8],
 a near-future Sci-Fi [roguelike] [2] and tactical squad game.
-Long-term goals are high replayability and auto-balancing
+Have a look at [PLAYING.md](GameDefinition/PLAYING.md) or jump straight
+into the fray.
+
+![gameplay screenshot](GameDefinition/screenshot.png?raw=true)
+
+Long-term goals of the project are high replayability and auto-balancing
 through procedural content generation and persistent content
 modification based on player behaviour. The game is written in [Haskell] [1]
 using the [LambdaHack] [5] roguelike game engine.
@@ -22,38 +27,38 @@
 
 For a newer version, install a matching LambdaHack library snapshot
 from a development branch, download the game source from [github] [3]
-and run `cabal install` from the main directory (for sample commands
-see file `.travis.yml`).
+and run `cabal install` from the main directory.
 
 
 Compatibility notes
 -------------------
 
-The current code was tested with GHC 7.6 and 7.8, but should also work with
-other GHC versions (see file `.travis.yml.7.4.2` for GHC 7.4 commands).
+The current code was tested with GHC 7.6 and 7.8,
+but should also work with other GHC versions.
 
 If you are using the terminal frontends, numerical keypad may not work
 correctly depending on versions of the libraries, terminfo and terminal
 emulators. The curses frontend is not fully supported due to the limitations
 of the curses library. With the vty frontend run in an xterm,
 CTRL-keypad keys for running seem to work OK, but on rxvt they do not.
-Vi keys (ykuhlbjn) should work everywhere regardless. GTK works fine, too.
+Laptop (uk8o79jl) and Vi keys (hjklyubn, if enabled in config.ui.ini)
+should work everywhere regardless. GTK works fine, too.
 
 
 Testing and debugging
 ---------------------
 
-The `Makefile` contains many sample test commands. All that use the screensaver
-game modes (AI vs. AI) and the simplest stdout frontend are gathered
-in `make test`. Of these, travis runs one of the sets prefixed
-`test-travis` on each push to the repo. Commands with prefix
-`frontend` run AI vs. AI games with the standard, user-friendly frontend.
-Commands with prefix `peek` set up a game mode where the player peeks
-into AI moves each time an AI actor dies or autosave kicks in.
+The [Makefile](Makefile) contains many sample test commands.
+All commands that use the screensaver game modes (AI vs. AI)
+ and the dumb `stdout` frontend are gathered in `make test`.
+Of these, travis runs `test-travis-*` on each push to the repo.
+Test commands with prefix `frontend` start AI vs. AI games
+with the standard, user-friendly frontend.
+
 Run `Allure --help` to see a brief description of all debug options.
 Of these, `--sniffIn` and `--sniffOut` are very useful (though verbose
 and initially cryptic), for monitoring the traffic between clients
-and the server. Some options in the config file may turn out useful too,
+and the server. Some options in the config file may prove useful too,
 though they mostly overlap with commandline options (and will be totally
 merged at some point).
 
@@ -62,7 +67,8 @@
 -------------------
 
 For more information, visit the [wiki] [6]
-and see `GameDefinition/PLAYING.md`, `CREDITS` and `LICENSE`.
+and see [PLAYING.md](GameDefinition/PLAYING.md), [CREDITS](CREDITS)
+and [LICENSE](LICENSE).
 
 Have fun!
 
@@ -90,8 +96,9 @@
 
 [1]: http://www.haskell.org/
 [2]: http://roguebasin.roguelikedevelopment.org/index.php?title=Berlin_Interpretation
-[3]: http://github.com/Mikolaj/Allure
+[3]: http://github.com/AllureOfTheStars/Allure
 [4]: http://hackage.haskell.org/package/Allure
-[5]: http://github.com/kosmikus/LambdaHack
-[6]: https://github.com/Mikolaj/Allure/wiki
+[5]: http://github.com/LambdaHack/LambdaHack
+[6]: https://github.com/AllureOfTheStars/Allure/wiki
 [7]: http://www.haskell.org/platform
+[8]: http://allureofthestars.com
diff --git a/changelog b/changelog
deleted file mode 100644
--- a/changelog
+++ /dev/null
@@ -1,50 +0,0 @@
-v0.4.12
-
-        * make walls lit by default to simplify exploration
-        * improve and simplify dungeon generation
-        * simplify running and permit multi-actor runs
-        * let items explode and generate shrapnel projectiles
-        * add game difficulty setting (initial HP scaling right now)
-        * allow recording, playing back and looping commands
-        * implement pathfinding via per-actor BFS over the whole level
-        * extend setting targets for actors in UI tremendously
-        * implement autoexplore, go-to-target, etc., as macros
-        * let AI use pathfinding, switch leaders, pick levels to swarm to
-        * force level/leader changes on spawners (even when played by humans)
-        * extend and redesign UI bottom status lines
-
-v0.4.10
-
-        * screensaver game modes (AI vs AI)
-        * improved AI (can now climbs stairs, etc.)
-        * multiple, multi-floor staircases
-        * multiple savefiles
-        * configurable framerate and combat animations
-
-v0.4.8
-
-        * experimental multiplayer modes
-        * a lot of gameplay changes induced by the engine overhaul and in particular the client-server rewrite
-
-v0.4.6.5
-
-        * this is a minor release, primarily intended to fix the broken compilation on Hackage
-        * changes since 0.4.6 are mostly unrelated to gameplay:
-          - strictly typed config files split into UI and rules
-          - a switch from Text to String throughout the codebase
-          - use of the external library miniutter for English sentence generation
-
-v0.4.6
-
-        * the Main Menu
-        * improved and configurable mode of squad combat
-
-v0.4.4
-
-        * missiles flying for three turns (by an old kosmikus' idea)
-        * visual feedback for targeting
-        * animations of combat and individual monster moves
-
-v0.4.3
-
-        * the Allure of the Stars game depends on the LambdaHack engine library
diff --git a/test/test.hs b/test/test.hs
new file mode 100644
--- /dev/null
+++ b/test/test.hs
@@ -0,0 +1,6 @@
+import TieKnot
+
+main :: IO ()
+main = do
+  tieKnot $ tail $ words "dist/build/LambdaHack/LambdaHack --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfter 6 --automateAll --gameMode campaign --difficulty 1 --setDungeonRng 42 --setMainRng 42"
+  -- tieKnot $ tail $ words "dist/build/LambdaHack/LambdaHack --dbgMsgSer --savePrefix test --newGame --noMore --noDelay --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfter 6 --automateAll --gameMode battle --difficulty 1 --setDungeonRng 42 --setMainRng 42"
