diff --git a/.travis.yml b/.travis.yml
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,14 @@
 language: haskell
 
 install:
+  - cd ..
+  - git clone --depth=50 --branch=mikolaj git://github.com/kosmikus/LambdaHack.git
+  - cd LambdaHack
   - cabal install gtk2hs-buildtools
-  - cabal install ConfigFile regex-posix regex-compat gtk --enable-tests --reinstall --force-reinstalls --constraint="mtl>=2.1.2" --constraint="containers>=0.5.2.1" --constraint="template-haskell>=2.7.0.0" --constraint="text>=0.11.2.3"
-  - cabal install LambdaHack --enable-tests --force-reinstalls --constraint="mtl>=2.1.2" --constraint="containers>=0.5.2.1" --constraint="template-haskell>=2.7.0.0" --constraint="text>=0.11.2.3"
-  - cabal install --enable-tests --force-reinstalls --constraint="mtl>=2.1.2" --constraint="containers>=0.5.2.1" --constraint="template-haskell>=2.7.0.0" --constraint="text>=0.11.2.3"
-  - make test-travis
+  - cabal install
+  - cd ../Allure
+  - cabal install --only-dependencies
+
+script:
+  - cabal install
+  - make test-travis || (cat ~/.Allure/config.rules.dump ; tail -n 200 /tmp/stdtest.log ; exit 77)
diff --git a/Allure.cabal b/Allure.cabal
--- a/Allure.cabal
+++ b/Allure.cabal
@@ -1,12 +1,12 @@
 name:          Allure
-version:       0.4.10
+version:       0.4.10.5
 synopsis:      Near-future roguelike game in very early and active 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.
                .
-               New in this release are screensaver game modes (AI vs AI),
+               New since 0.4.8 are screensaver game modes (AI vs AI),
                improved AI (can now climbs stairs, etc.), multiple,
                multi-floor staircases, multiple savefiles, configurable
                framerate and combat animations and more.
@@ -20,10 +20,10 @@
 bug-reports:   http://github.com/Mikolaj/Allure/issues
 license:       OtherLicense
 license-file:  LICENSE
-tested-with:   GHC == 7.4.2, GHC == 7.6.1
-data-files:    LICENSE, CREDITS, PLAYING.md, README.md,
-               config.rules.default, config.ui.default, MainMenu.ascii, scores,
-               Makefile, .travis.yml
+tested-with:   GHC == 7.4.2, GHC == 7.6.3, GHC == 7.7
+data-files:    config.rules.default, config.ui.default, scores
+extra-source-files: LICENSE, CREDITS, changelog, PLAYING.md, README.md,
+                    Makefile, MainMenu.ascii, .travis.yml
 author:        Andres Loeh, Mikolaj Konarski and others
 maintainer:    Mikolaj Konarski <mikolaj.konarski@funktory.com>
 category:      Game
@@ -46,7 +46,7 @@
                       Content.RuleKind,
                       Content.TileKind,
                       Paths_Allure
-  build-depends:      LambdaHack >= 0.2.10 && < 0.2.11,
+  build-depends:      LambdaHack >= 0.2.10.5 && < 0.2.11,
                       template-haskell >= 2.6 && < 3,
 
                       ConfigFile >= 1.1.1   && < 2,
@@ -57,7 +57,7 @@
                       containers >= 0.5     && < 1,
                       deepseq    >= 1.3     && < 2,
                       directory  >= 1.1.0.1 && < 2,
-                      enummapset >= 0.5.2   && < 1,
+                      enummapset-th >= 0.6.0.0 && < 1,
                       filepath   >= 1.2.0.1 && < 2,
                       ghc-prim   >= 0.2,
                       hashable   >= 1.2     && < 2,
@@ -67,7 +67,7 @@
                       old-time   >= 1.0.0.7 && < 2,
                       random     >= 1.0.1   && < 2,
                       stm        >= 2.4     && < 3,
-                      text       >= 0.11.2.3 && < 1,
+                      text       >= 0.11.2.3 && < 2,
                       transformers >= 0.3   && < 1,
                       unordered-containers >= 0.2.3 && < 1,
                       zlib       >= 0.5.3.1 && < 1
@@ -75,7 +75,7 @@
   default-language:   Haskell2010
   default-extensions: MonoLocalBinds, ScopedTypeVariables, OverloadedStrings
                       BangPatterns, RecordWildCards, NamedFieldPuns
-  other-extensions:   CPP, TemplateHaskell
+  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
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -4,66 +4,146 @@
 # For license and copyright information, see the file LICENSE.
 #
 
-test: test-short test-medium test-long
+# All xc* tests assume a profiling build (for stack traces).
+# See the install-debug target below or .travis.yml.prof.
 
-test-long: testCampaign-long testCoop-long testDefense-long
+install-debug:
+	cabal install --enable-library-profiling --enable-executable-profiling --ghc-options="-fprof-auto-calls" --disable-optimization
 
-test-medium: testCampaign-medium testCoop-medium testDefense-medium
+configure-debug:
+	cabal configure --enable-library-profiling --enable-executable-profiling --ghc-options="-fprof-auto-calls" --disable-optimization
 
-testCampaign-long:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --gameMode screensaver --frontendStd --stopAfter 500 > /tmp/stdtest.log
 
-testCampaign-medium:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --gameMode screensaver --frontendStd --stopAfter 60 > /tmp/stdtest.log
+xcplay:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer
 
-frontendCampaign:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 45 --savePrefix test --gameMode screensaver
+xcpeekCampaign:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix peekCampaign --gameMode peekCampaign
 
-testCoop-long:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --fovMode Permissive --savePrefix test --gameMode testCoop --frontendStd --stopAfter 500 > /tmp/stdtest.log
+xcpeekSkirmish:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix peekSkirmish --gameMode peekSkirmish
 
-testCoop-medium:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --fovMode Shadow --savePrefix test --gameMode testCoop --frontendStd --stopAfter 60 > /tmp/stdtest.log
+xcfrontendCampaign:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 45 --savePrefix test --gameMode screensaver
 
-frontendCoop:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 180 --fovMode Permissive --savePrefix test --gameMode testCoop
+xcfrontendCoop:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 180 --fovMode Permissive --savePrefix test --gameMode testCoop
 
-testDefense-long:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noAnim --maxFps 100000 --savePrefix test --gameMode testDefense --frontendStd --stopAfter 500 > /tmp/stdtest.log
+xcfrontendDefense:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 45 --savePrefix test --gameMode testDefense
 
-testDefense-medium:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 100000 --savePrefix test --gameMode testDefense --frontendStd --stopAfter 60 > /tmp/stdtest.log
 
-frontendDefense:
-	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 45 --savePrefix test --gameMode testDefense
+xctest-travis: xctest-short xctest-medium
 
-test-short: test-short-new test-short-load
+xctest: xctest-short xctest-medium xctest-long
 
-test-short-new:
-	yes . | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix campaign --gameMode campaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix skirmish --gameMode skirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix PvP --gameMode PvP --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix Coop --gameMode Coop --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix defense --gameMode defense --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix peekCampaign --gameMode peekCampaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix peekSkirmish --gameMode peekSkirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
+xctest-short: xctest-short-new xctest-short-load
 
-test-short-load:
-	yes . | dist/build/Allure/Allure --dbgMsgSer --savePrefix campaign --gameMode campaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --savePrefix skirmish --gameMode skirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --savePrefix PvP --gameMode PvP --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --savePrefix Coop --gameMode Coop --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --savePrefix defense --gameMode defense --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --savePrefix peekCampaign --gameMode peekCampaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
-	yes . | dist/build/Allure/Allure --dbgMsgSer --savePrefix peekSkirmish --gameMode peekSkirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
+xctest-medium: xctestCampaign-medium xctestCoop-medium xctestDefense-medium
 
-test-travis: test-short test-medium
+xctest-long: xctestCampaign-long xctestCoop-long xctestDefense-long
 
+xctestCampaign-long:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --gameMode screensaver --frontendStd --stopAfter 1000 > /tmp/stdtest.log
+
+xctestCampaign-medium:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --gameMode screensaver --frontendStd --dumpConfig --stopAfter 200 > /tmp/stdtest.log
+
+xctestCoop-long:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --fovMode Permissive --savePrefix test --gameMode testCoop --frontendStd --stopAfter 1000 > /tmp/stdtest.log
+
+xctestCoop-medium:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --fovMode Shadow --savePrefix test --gameMode testCoop --frontendStd --dumpConfig --stopAfter 200 > /tmp/stdtest.log
+
+xctestDefense-long:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --noAnim --maxFps 100000 --savePrefix test --gameMode testDefense --frontendStd --stopAfter 1000 > /tmp/stdtest.log
+
+xctestDefense-medium:
+	dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --newGame --noMore --maxFps 100000 --savePrefix test --gameMode testDefense --frontendStd --dumpConfig --stopAfter 200 > /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 --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 --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 PvP --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 --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 --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 --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 --gameMode peekSkirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
+
+xctest-short-load:
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure +RTS -xc -RTS --dbgMsgSer --savePrefix campaign --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 --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 PvP --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 --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 --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 --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 --gameMode peekSkirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
+
+
+play:
+	dist/build/Allure/Allure --dbgMsgSer
+
 peekCampaign:
 	dist/build/Allure/Allure --dbgMsgSer --savePrefix peekCampaign --gameMode peekCampaign
 
 peekSkirmish:
 	dist/build/Allure/Allure --dbgMsgSer --savePrefix peekSkirmish --gameMode peekSkirmish
+
+frontendCampaign:
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 45 --savePrefix test --gameMode screensaver
+
+frontendCoop:
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 180 --fovMode Permissive --savePrefix test --gameMode testCoop
+
+frontendDefense:
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 45 --savePrefix test --gameMode testDefense
+
+
+test-travis: test-short test-medium
+
+test: test-short test-medium test-long
+
+test-short: test-short-new test-short-load
+
+test-medium: testCampaign-medium testCoop-medium testDefense-medium
+
+test-long: testCampaign-long testCoop-long testDefense-long
+
+testCampaign-long:
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --gameMode screensaver --frontendStd --stopAfter 1000 > /tmp/stdtest.log
+
+testCampaign-medium:
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --savePrefix test --gameMode screensaver --frontendStd --dumpConfig --stopAfter 200 > /tmp/stdtest.log
+
+testCoop-long:
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --fovMode Permissive --savePrefix test --gameMode testCoop --frontendStd --stopAfter 1000 > /tmp/stdtest.log
+
+testCoop-medium:
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noDelay --noAnim --maxFps 100000 --fovMode Shadow --savePrefix test --gameMode testCoop --frontendStd --dumpConfig --stopAfter 200 > /tmp/stdtest.log
+
+testDefense-long:
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --noAnim --maxFps 100000 --savePrefix test --gameMode testDefense --frontendStd --stopAfter 1000 > /tmp/stdtest.log
+
+testDefense-medium:
+	dist/build/Allure/Allure --dbgMsgSer --newGame --noMore --maxFps 100000 --savePrefix test --gameMode testDefense --frontendStd --dumpConfig --stopAfter 200 > /tmp/stdtest.log
+
+test-short-new:
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix campaign --gameMode campaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix skirmish --gameMode skirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix PvP --gameMode PvP --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix Coop --gameMode Coop --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix defense --gameMode defense --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix peekCampaign --gameMode peekCampaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --newGame --savePrefix peekSkirmish --gameMode peekSkirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
+
+test-short-load:
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix campaign --gameMode campaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix skirmish --gameMode skirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix PvP --gameMode PvP --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix Coop --gameMode Coop --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix defense --gameMode defense --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix peekCampaign --gameMode peekCampaign --frontendStd --stopAfter 0 > /tmp/stdtest.log
+	while true; do echo ' '; echo '.'; sleep 1; done | dist/build/Allure/Allure --dbgMsgSer --savePrefix peekSkirmish --gameMode peekSkirmish --frontendStd --stopAfter 0 > /tmp/stdtest.log
 
 
 # The rest of the makefile is unmaintained at the moment.
diff --git a/PLAYING.md b/PLAYING.md
--- a/PLAYING.md
+++ b/PLAYING.md
@@ -32,7 +32,8 @@
                terrain type                       on-screen symbol
                floor                              .
                wall                               #
-               wall cache                         &
+               rock or tree                       O
+               cache                              &
                stairs up                          <
                stairs down                        >
                open door                          '
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -26,8 +26,8 @@
 Compatibility notes
 -------------------
 
-The current code was tested with GHC 7.6, but should also work with
-other GHC versions (see file .travis.yml for GHC 7.4 commands).
+The current code was tested with GHC 7.6.3, but should also work with
+other GHC versions (see file .travis.yml.7.4.2 for GHC 7.4.2 commands).
 
 If you are using the curses or vty frontends,
 numerical keypad may not work correctly depending on the versions
diff --git a/changelog b/changelog
new file mode 100644
--- /dev/null
+++ b/changelog
@@ -0,0 +1,41 @@
+0.4.3
+
+	* the Allure of the Stars game depends on the LambdaHack engine library
+
+0.4.4
+
+	* missiles flying for three turns (by an old kosmikus' idea)
+
+	* visual feedback for targeting
+
+	* animations of combat and individual monster moves
+
+0.4.6
+
+	* the Main Menu
+
+	* improved and configurable mode of squad combat
+
+0.4.6.5
+
+	* this is a minor release, primarily intended to fix the broken compilation on Hackage
+
+	* changes since 0.2.6 are mostly in the engine and are 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
+
+0.4.8
+
+	* experimental multiplayer modes
+
+	* a lot of gameplay changes induced by the engine overhaul and in particular the client-server rewrite
+
+0.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
diff --git a/config.ui.default b/config.ui.default
--- a/config.ui.default
+++ b/config.ui.default
@@ -32,7 +32,6 @@
 ; CTRL-e: GameRestart "defense"
 ; CTRL-x: GameExit
 ; CTRL-s: GameSave
-; CTRL-d: CfgDump
 ; Tab: MemberCycle
 ; ISO_Left_Tab: MemberBack
 ; I: Inventory
diff --git a/src/Content/CaveKind.hs b/src/Content/CaveKind.hs
--- a/src/Content/CaveKind.hs
+++ b/src/Content/CaveKind.hs
@@ -30,19 +30,23 @@
   , cfreq         = [("dng", 100), ("caveRogue", 1)]
   , cxsize        = fst normalLevelBound + 1
   , cysize        = snd normalLevelBound + 1
-  , cgrid         = RollDiceXY (rollDice 2 3, rollDice 2 2)
-  , cminPlaceSize = RollDiceXY (rollDice 2 2, rollDice 2 1)
-  , cdarkChance   = (rollDice 1 54, rollDice 0 0)
+  , 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
   , cauxConnects  = 1%3
-  , cvoidChance   = 1%4
-  , cnonVoidMin   = 4
+  , cmaxVoid      = 1%6
   , cminStairDist = 30
   , cdoorChance   = 1%2
   , copenChance   = 1%10
   , chidden       = 8
   , citemNum      = rollDice 7 2
+  , citemFreq     = [(70, "useful"), (30, "treasure")]
   , cdefTile        = "fillerWall"
-  , ccorridorTile   = "darkCorridor"
+  , cdarkCorTile    = "floorCorridorDark"
+  , clitCorTile     = "floorCorridorLit"
   , cfillerTile     = "fillerWall"
   , cdarkLegendTile = "darkLegend"
   , clitLegendTile  = "litLegend"
@@ -51,57 +55,67 @@
   { csymbol       = 'A'
   , cname         = "Recreational deck"
   , cfreq         = [("dng", 100), ("caveArena", 1)]
-  , cgrid         = RollDiceXY (rollDice 2 2, rollDice 2 2)
-  , cminPlaceSize = RollDiceXY (rollDice 3 2, rollDice 2 1)
-  , cdarkChance   = (rollDice 1 80, rollDice 1 60)
-  , cvoidChance   = 1%3
-  , cnonVoidMin   = 2
-  , chidden       = 9
-  , citemNum      = rollDice 4 2  -- few rooms
-  , cdefTile      = "floorArenaLit"
-  , ccorridorTile = "path"
+  , cgrid         = rollDiceXY [(2, 2)] [(2, 2)]
+  , cminPlaceSize = rollDiceXY [(2, 2), (3, 1)] [(4, 1)]
+  , cdarkChance   = rollDeep (1, 80) (1, 60)
+  , cnightChance  = intToDeep 0
+  , cmaxVoid      = 1%3
+  , chidden       = 1000
+  , citemNum      = rollDice 5 2  -- few rooms
+  , cdefTile      = "arenaSet"
+  , cdarkCorTile  = "pathDark"
+  , clitCorTile   = "pathLit"
   }
 empty = rogue
   { csymbol       = '.'
   , cname         = "Construction site"
   , cfreq         = [("dng", 100), ("caveEmpty", 1)]
-  , cgrid         = RollDiceXY (rollDice 2 2, rollDice 1 2)
-  , cminPlaceSize = RollDiceXY (rollDice 4 3, rollDice 4 1)
-  , cdarkChance   = (rollDice 1 80, rollDice 1 80)
+  , 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
   , cauxConnects  = 1
-  , cvoidChance   = 3%4
-  , cnonVoidMin   = 1
+  , cmaxVoid      = 1%2
   , cminStairDist = 50
-  , chidden       = 10
+  , chidden       = 1000
   , citemNum      = rollDice 8 2  -- whole floor strewn with treasure
-  , cdefTile      = "floorRoomLit"
-  , ccorridorTile = "floorRoomLit"
+  , cdefTile      = "emptySet"
+  , cdarkCorTile  = "pathDark"
+  , clitCorTile   = "floorArenaLit"
   }
 noise = rogue
   { csymbol       = '!'
   , cname         = "Machine rooms"
   , cfreq         = [("dng", 100), ("caveNoise", 1)]
-  , cgrid         = RollDiceXY (rollDice 2 2, rollDice 1 2)
-  , cminPlaceSize = RollDiceXY (rollDice 4 2, rollDice 4 1)
-  , cdarkChance   = (rollDice 1 80, rollDice 1 40)
-  , cvoidChance   = 0
-  , cnonVoidMin   = 0
-  , chidden       = 6
-  , citemNum      = rollDice 4 2  -- few rooms
+  , 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)
+  , cmaxVoid      = 0
+  , chidden       = 1000
+  , citemNum      = rollDice 4 2  -- fewer rooms
   , cdefTile      = "noiseSet"
-  , ccorridorTile = "path"
+  , cdarkCorTile  = "pathDark"
+  , clitCorTile   = "pathLit"
   }
 combat = rogue
   { csymbol       = 'C'
   , cname         = "Combat arena"
   , cfreq         = [("caveCombat", 1)]
-  , cgrid         = RollDiceXY (rollDice 5 2, rollDice 2 2)
-  , cminPlaceSize = RollDiceXY (rollDice 1 1, rollDice 1 1)
-  , cdarkChance   = (rollDice 1 100, rollDice 1 100)
-  , cvoidChance   = 1%10
-  , cnonVoidMin   = 8
-  , chidden       = 100
+  , 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
+  , cdoorChance   = 1
+  , copenChance   = 0
   , citemNum      = rollDice 12 2
+  , citemFreq     = [(100, "useful")]
   , cdefTile      = "combatSet"
-  , ccorridorTile = "path"
+  , cdarkCorTile  = "pathLit"  -- for now, let paths give off light
+  , clitCorTile   = "floorArenaLit"
   }
diff --git a/src/Content/ItemKind.hs b/src/Content/ItemKind.hs
--- a/src/Content/ItemKind.hs
+++ b/src/Content/ItemKind.hs
@@ -28,7 +28,7 @@
 
 gem, potion, scroll, wand :: ItemKind  -- generic templates
 
--- castDeep (aDb, xDy) = castDice aDb + lvl * castDice xDy / depth
+-- castDeep (aDb, xDy) = castDice aDb + (lvl - 1) * castDice xDy / (depth - 1)
 
 {- Item group symbols (from Angband, only as an informal convention for now):
 
@@ -56,9 +56,9 @@
 necklace = ItemKind
   { isymbol  = '"'
   , iname    = "necklace"
-  , ifreq    = [("dng", 6)]
+  , ifreq    = [("useful", 6)]
   , iflavour = zipFancy [BrGreen]
-  , ieffect  = Regeneration (rollDice 2 3, rollDice 1 10)
+  , ieffect  = Regeneration (rollDeep (2, 3) (1, 10))
   , icount   = intToDeep 1
   , iverbApply   = "tear down"
   , iverbProject = "cast"
@@ -68,10 +68,10 @@
 dart = ItemKind
   { isymbol  = '{'
   , iname    = "billiard ball"
-  , ifreq    = [("dng", 30)]
+  , ifreq    = [("useful", 20)]
   , iflavour = zipPlain [BrWhite]
-  , ieffect  = Hurt (rollDice 1 1) (rollDice 1 2, rollDice 1 2)
-  , icount   = (rollDice 3 3, rollDice 0 0)
+  , ieffect  = Hurt (rollDice 1 1) (rollDeep (1, 2) (1, 2))
+  , icount   = rollDeep (3, 3) (0, 0)
   , iverbApply   = "splinter"
   , iverbProject = "hurl"
   , iweight  = 50
@@ -80,7 +80,7 @@
 gem = ItemKind
   { isymbol  = '*'
   , iname    = "precious gem"
-  , ifreq    = [("dng", 20)]       -- x3, but rare on shallow levels
+  , ifreq    = [("treasure", 20)]  -- x3, but rare on shallow levels
   , iflavour = zipPlain $ L.delete BrYellow brightCol  -- natural, so not fancy
   , ieffect  = NoEffect
   , icount   = intToDeep 0
@@ -90,21 +90,21 @@
   , itoThrow = 0
   }
 gem1 = gem
-  { icount   = (rollDice 0 0, rollDice 1 1)  -- appears on max depth
+  { icount   = rollDeep (0, 0) (1, 1)  -- appears on max depth
   }
 gem2 = gem
-  { icount   = (rollDice 0 0, rollDice 1 2)  -- appears halfway, doubled on max
+  { icount   = rollDeep (0, 0) (1, 2)  -- appears halfway
   }
 gem3 = gem
-  { icount   = (rollDice 0 0, rollDice 1 3)
+  { icount   = rollDeep (0, 0) (1, 3)  -- appears early
   }
 currency = ItemKind
   { isymbol  = '$'
   , iname    = "gold grain"
-  , ifreq    = [("dng", 50), ("currency", 1)]
+  , ifreq    = [("treasure", 20), ("currency", 1)]
   , iflavour = zipPlain [BrYellow]
   , ieffect  = NoEffect
-  , icount   = (rollDice 0 0, rollDice 10 10)
+  , icount   = rollDeep (0, 0) (10, 10)  -- appears on lvl 2
   , iverbApply   = "smear"
   , iverbProject = "blow away"
   , iweight  = 1
@@ -113,10 +113,10 @@
 javelin = ItemKind
   { isymbol  = '{'
   , iname    = "javelin"
-  , ifreq    = [("dng", 30)]
+  , ifreq    = [("useful", 30)]
   , iflavour = zipPlain [Brown]
-  , ieffect  = Hurt (rollDice 1 2) (rollDice 1 2, rollDice 2 2)
-  , icount   = (rollDice 0 0, rollDice 1 1)
+  , ieffect  = Hurt (rollDice 1 2) (rollDeep (1, 2) (2, 2))
+  , icount   = rollDeep (0, 0) (1, 1)
   , iverbApply   = "break up"
   , iverbProject = "hurl"
   , iweight  = 2000
@@ -125,10 +125,10 @@
 kitchenKnife = ItemKind
   { isymbol  = '{'
   , iname    = "kitchen knife"
-  , ifreq    = [("dng", 30)]
+  , ifreq    = [("useful", 25)]
   , iflavour = zipPlain [BrCyan]
-  , ieffect  = Hurt (rollDice 1 1) (rollDice 0 0, rollDice 1 2)
-  , icount   = (rollDice 0 0, rollDice 1 2)
+  , ieffect  = Hurt (rollDice 1 1) (rollDeep (0, 0) (1, 2))
+  , icount   = rollDeep (0, 0) (1, 2)
   , iverbApply   = "bend"
   , iverbProject = "throw"
   , iweight  = 200
@@ -137,7 +137,7 @@
 potion = ItemKind
   { isymbol  = '!'
   , iname    = "vial"
-  , ifreq    = [("dng", 15)]
+  , ifreq    = [("useful", 15)]
   , iflavour = zipFancy stdCol
   , ieffect  = NoEffect
   , icount   = intToDeep 1
@@ -147,22 +147,22 @@
   , itoThrow = -50  -- oily, bad grip
   }
 potion1 = potion
-  { ifreq    = [("dng", 5)]
+  { ifreq    = [("useful", 5)]
   , ieffect  = ApplyPerfume
   }
 potion2 = potion
   { ieffect  = Heal 5
   }
 potion3 = potion
-  { ifreq    = [("dng", 5)]
+  { ifreq    = [("useful", 5)]
   , ieffect  = Heal (-5)
   }
 ring = ItemKind
   { isymbol  = '='
   , iname    = "ring"
-  , ifreq    = []  -- [("dng", 10)]  -- TODO: make it useful
+  , ifreq    = []  -- [("useful", 10)]  -- TODO: make it useful
   , iflavour = zipPlain [White]
-  , ieffect  = Searching (rollDice 1 6, rollDice 3 2)
+  , ieffect  = Searching (rollDeep (1, 6) (3, 2))
   , icount   = intToDeep 1
   , iverbApply   = "squeeze down"
   , iverbProject = "toss"
@@ -172,7 +172,7 @@
 scroll = ItemKind
   { isymbol  = '?'
   , iname    = "tablet"
-  , ifreq    = [("dng", 4)]
+  , ifreq    = [("useful", 4)]
   , iflavour = zipFancy darkCol  -- arcane and old
   , ieffect  = NoEffect
   , icount   = intToDeep 1
@@ -193,9 +193,9 @@
 sword = ItemKind
   { isymbol  = '/'
   , iname    = "sharpened pipe"
-  , ifreq    = [("dng", 40)]
+  , ifreq    = [("useful", 40)]
   , iflavour = zipPlain [Cyan]
-  , ieffect  = Hurt (rollDice 3 1) (rollDice 1 2, rollDice 4 2)
+  , ieffect  = Hurt (rollDice 3 1) (rollDeep (1, 2) (4, 2))
   , icount   = intToDeep 1
   , iverbApply   = "hit"
   , iverbProject = "heave"
@@ -205,7 +205,7 @@
 wand = ItemKind
   { isymbol  = '-'
   , iname    = "injector"
-  , ifreq    = [("dng", 15)]
+  , ifreq    = [("useful", 15)]
   , iflavour = zipFancy brightCol
   , ieffect  = NoEffect
   , icount   = intToDeep 1
@@ -218,7 +218,7 @@
   { ieffect  = Dominate
   }
 wand2 = wand
-  { ifreq    = [("dng", 3)]
+  { ifreq    = [("useful", 3)]
   , ieffect  = Heal (-25)
   }
 fist = sword
diff --git a/src/Content/ModeKind.hs b/src/Content/ModeKind.hs
--- a/src/Content/ModeKind.hs
+++ b/src/Content/ModeKind.hs
@@ -117,11 +117,11 @@
 
 playersSkirmish = Players
   { playersList = [ playerHero {playerName = "White"}
-                  , playerAntiHero {playerName = "Green"}
+                  , playerAntiHero {playerName = "Purple"}
                   , playerHorror ]
-  , playersEnemy = [ ("White", "Green")
+  , playersEnemy = [ ("White", "Purple")
                    , ("White", "Horror Den")
-                   , ("Green", "Horror Den") ]
+                   , ("Purple", "Horror Den") ]
   , playersAlly = [] }
 
 playersPvP = Players
@@ -237,12 +237,12 @@
 
 playersPeekSkirmish = Players
   { playersList = [ playerHero {playerName = "White"}
-                  , playerAntiHero { playerName = "Green"
+                  , playerAntiHero { playerName = "Purple"
                                    , playerUI = True }
                   , playerHorror ]
-  , playersEnemy = [ ("White", "Green")
+  , playersEnemy = [ ("White", "Purple")
                    , ("White", "Horror Den")
-                   , ("Green", "Horror Den") ]
+                   , ("Purple", "Horror Den") ]
   , playersAlly = [] }
 
 
diff --git a/src/Content/TileKind.hs b/src/Content/TileKind.hs
--- a/src/Content/TileKind.hs
+++ b/src/Content/TileKind.hs
@@ -19,23 +19,23 @@
   , getFreq = tfreq
   , validate = tvalidate
   , content =
-      [wall, wallCache, hardRock, pillar, wallSuspect, doorClosed, doorOpen, stairsUp, stairsDown, escapeUp, escapeDown, liftUp, lift, liftDown, unknown, floorCorridorLit, floorCorridorDark, floorItemLit, floorItemDark, floorActorItemLit, floorActorItemDark, floorRed, floorBlue, floorGreen]
+      [wall, wallCache, hardRock, pillar, tree, wallSuspect, doorClosed, doorOpen, stairsUp, stairsDown, escapeUp, escapeDown, liftUp, lift, liftDown, unknown, floorCorridorLit, floorCorridorDark, floorItemLit, floorItemDark, floorActorItemLit, floorActorItemDark, floorRedDark, floorBlueDark, floorGreenDark, floorRedLit, floorBlueLit, floorGreenLit]
   }
-wall,        wallCache, hardRock, pillar, wallSuspect, doorClosed, doorOpen, stairsUp, stairsDown, escapeUp, escapeDown, liftUp, lift, liftDown, unknown, floorCorridorLit, floorCorridorDark, floorItemLit, floorItemDark, floorActorItemLit, floorActorItemDark, floorRed, floorBlue, floorGreen :: TileKind
+wall,        wallCache, hardRock, pillar, tree, wallSuspect, doorClosed, doorOpen, stairsUp, stairsDown, escapeUp, escapeDown, liftUp, lift, liftDown, unknown, floorCorridorLit, floorCorridorDark, floorItemLit, floorItemDark, floorActorItemLit, floorActorItemDark, floorRedDark, floorBlueDark, floorGreenDark, floorRedLit, floorBlueLit, floorGreenLit :: TileKind
 
 wall = TileKind
   { tsymbol  = '#'
   , tname    = "granite wall"
   , tfreq    = [ ("fillerWall", 1), ("cachable", 70)
                , ("litLegend", 100), ("darkLegend", 100)
-               , ("noiseSet", 55), ("combatSet", 5) ]
+               , ("noiseSet", 55) ]
   , tcolor   = BrWhite
   , tcolor2  = defFG
   , tfeature = [HideAs "suspect wall"]
   }
 wallCache = TileKind
   { tsymbol  = '&'
-  , tname    = "wall cache"
+  , tname    = "cache"
   , tfreq    = [ ("cachable", 30)
                , ("litLegend", 100), ("darkLegend", 100) ]
   , tcolor   = BrWhite
@@ -44,20 +44,28 @@
   }
 hardRock = TileKind
   { tsymbol  = '#'
-  , tname    = "hard rock"
-  , tfreq    = [("hard rock", 1)]
+  , tname    = "outer hull"
+  , tfreq    = [("outer fence", 1)]
   , tcolor   = BrBlack
   , tcolor2  = BrBlack
   , tfeature = [Impenetrable]
   }
 pillar = TileKind
   { tsymbol  = 'O'
-  , tname    = "pillar"
-  , tfreq    = [("litLegend", 100), ("darkLegend", 100)]
+  , tname    = "rock"
+  , tfreq    = [("litLegend", 100), ("darkLegend", 100), ("combatSet", 3)]
   , tcolor   = BrWhite
   , tcolor2  = defFG
   , tfeature = []
   }
+tree = TileKind
+  { tsymbol  = 'O'
+  , tname    = "tree"
+  , tfreq    = [("combatSet", 8)]
+  , tcolor   = BrGreen
+  , tcolor2  = Green
+  , tfeature = []
+  }
 wallSuspect = TileKind
   { tsymbol  = '#'
   , tname    = "moldy wall"
@@ -153,19 +161,21 @@
 floorCorridorLit = TileKind
   { tsymbol  = '.'
   , tname    = "floor"
-  , tfreq    = [("floorArenaLit", 1), ("noiseSet", 100)]
+  , tfreq    = [ ("floorCorridorLit", 1), ("floorArenaLit", 1)
+               , ("arenaSet", 1), ("noiseSet", 100), ("combatSet", 100) ]
   , tcolor   = BrWhite
   , tcolor2  = defFG
   , tfeature = [Walkable, Clear, Lit]
   }
 floorCorridorDark = floorCorridorLit
-  { tfreq    = [("darkCorridor", 1)]
+  { tfreq    = [ ("floorCorridorDark", 1)
+               , ("arenaSet", 1), ("noiseSet", 100), ("combatSet", 100) ]
   , tcolor   = BrYellow
   , tcolor2  = BrBlack
   , tfeature = [Walkable, Clear]
   }
 floorItemLit = floorCorridorLit
-  { tfreq    = [("combatSet", 100)]
+  { tfreq    = []
   , tfeature = CanItem : tfeature floorCorridorLit
   }
 floorItemDark = floorCorridorDark
@@ -173,29 +183,41 @@
   , tfeature = CanItem : tfeature floorCorridorDark
   }
 floorActorItemLit = floorItemLit
-  { tfreq    = [("litLegend", 100), ("floorRoomLit", 1)]
+  { tfreq    = [("litLegend", 100), ("emptySet", 1)]
   , tfeature = CanActor : tfeature floorItemLit
   }
 floorActorItemDark = floorItemDark
-  { tfreq    = [("darkLegend", 100)]
+  { tfreq    = [("darkLegend", 100), ("emptySet", 1)]
   , tfeature = CanActor : tfeature floorItemDark
   }
-floorRed = floorCorridorLit
+floorRedDark = floorCorridorDark
   { tname    = "emergency walkway"
-  , tfreq    = [("path", 20)]
+  , tfreq    = [("pathDark", 20)]
   , tcolor   = BrRed
   , tcolor2  = Red
-  , tfeature = Path : tfeature floorCorridorLit
+  , tfeature = Path : tfeature floorCorridorDark
   }
-floorBlue = floorRed
+floorRedLit  = floorRedDark
+  { tfreq    = [("pathLit", 20)]
+  , tfeature = Lit : tfeature floorRedDark
+  }
+floorBlueDark = floorRedDark
   { tname    = "transport route"
-  , tfreq    = [("path", 100)]
+  , tfreq    = [("pathDark", 100)]
   , tcolor   = BrBlue
   , tcolor2  = Blue
   }
-floorGreen = floorRed
+floorBlueLit = floorBlueDark
+  { tfreq    = [("pathLit", 100)]
+  , tfeature = Lit : tfeature floorBlueDark
+  }
+floorGreenDark = floorRedDark
   { tname    = "greenery path"
-  , tfreq    = [("path", 100)]
+  , tfreq    = [("pathDark", 100)]
   , tcolor   = BrGreen
   , tcolor2  = Green
+  }
+floorGreenLit = floorGreenDark
+  { tfreq    = [("pathLit", 100)]
+  , tfeature = Lit : tfeature floorGreenDark
   }
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -27,9 +27,13 @@
 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
 
+-- | 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 ()
@@ -39,13 +43,13 @@
 main :: IO ()
 main =
   let copsSlow = Kind.COps
-        { coactor = Kind.createOps Content.ActorKind.cdefs
-        , cocave  = Kind.createOps Content.CaveKind.cdefs
-        , cofact  = 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
+        { 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
