diff --git a/Allure.cabal b/Allure.cabal
--- a/Allure.cabal
+++ b/Allure.cabal
@@ -1,16 +1,13 @@
 name:          Allure
-version:       0.4.6.5
+version:       0.4.8
 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 since 0.4.4 are the Main Menu and improved squad combat.
-               If you use GHC 7.6.1 and gtk2hs compiles for you,
-               please install the LambdaHack library manually,
-               with 'cabal install LambdaHack -fgtk --reinstall'.
-               .
+               New in this release are experimental multiplayer modes
+               and a lot of gameplay changes induced by the engine overhaul.
                Long term goals are high replayability and auto-balancing
                through procedural content generation and persistent content
                modification based on player behaviour.
@@ -21,10 +18,9 @@
 bug-reports:   http://github.com/Mikolaj/Allure/issues
 license:       OtherLicense
 license-file:  LICENSE
-tested-with:   GHC == 7.2.2, GHC == 7.4.1, GHC == 7.6.1
+tested-with:   GHC == 7.4.2, GHC == 7.6.1
 data-files:    LICENSE, CREDITS, PLAYING.md, README.md,
-               config.rules.default, config.ui.default, config.rules.bot,
-               scores
+               config.rules.default, config.ui.default, scores
 author:        Andres Loeh, Mikolaj Konarski and others
 maintainer:    Mikolaj Konarski <mikolaj.konarski@funktory.com>
 category:      Game
@@ -48,28 +44,36 @@
                       Content.TileKind,
                       Multiline,
                       Paths_Allure
-  build-depends:      LambdaHack >= 0.2.6.5 && < 0.2.7,
+  build-depends:      LambdaHack >= 0.2.8 && < 0.2.9,
                       template-haskell >= 2.6 && < 3,
 
                       ConfigFile >= 1.1.1   && < 2,
                       array      >= 0.3.0.3 && < 1,
                       base       >= 4       && < 5,
-                      binary     >= 0.5.0.2 && < 1,
+                      binary     >= 0.7     && < 1,
                       bytestring >= 0.9.2   && < 1,
-                      containers >= 0.4.1   && < 1,
+                      containers >= 0.5     && < 1,
+                      deepseq    >= 1.3     && < 2,
                       directory  >= 1.1.0.1 && < 2,
+                      enummapset >= 0.5.2   && < 1,
                       filepath   >= 1.2.0.1 && < 2,
-                      miniutter  >= 0.4.0   && < 2,
+                      ghc-prim   >= 0.2,
+                      hashable   >= 1.2     && < 2,
+                      keys       >= 3       && < 4,
+                      miniutter  >= 0.4.1   && < 2,
                       mtl        >= 2.0.1   && < 3,
                       old-time   >= 1.0.0.7 && < 2,
                       random     >= 1.0.1   && < 2,
+                      stm        >= 2.4     && < 3,
                       text       >= 0.11.2.3 && < 1,
+                      transformers >= 0.3   && < 1,
+                      unordered-containers >= 0.2.3 && < 1,
                       zlib       >= 0.5.3.1 && < 1
 
   default-language:   Haskell2010
-  default-extensions: MonoLocalBinds,
+  default-extensions: MonoLocalBinds, ScopedTypeVariables,
                       BangPatterns, RecordWildCards, NamedFieldPuns
-  other-extensions:   CPP, QuasiQuotes, OverloadedStrings, ExtendedDefaultRules
+  other-extensions:   CPP, QuasiQuotes, OverloadedStrings
   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/PLAYING.md b/PLAYING.md
--- a/PLAYING.md
+++ b/PLAYING.md
@@ -14,17 +14,20 @@
 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 multiplayer cooperative and competitive modes,
+though only a shared-screen interface is provided at this time.
 Contributions welcome.
 
 
 Terrain
 -------
 
-The heroes are marked on the map with symbol '@' and with '1', '2', ..., '9'.
+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,
 battle the horrors within, gather as much gold and precious gems
-as possible, and escape to tell the tale. The spaceship consists
-of many levels covered with varying terrain of the following basic kinds:
+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:
 
                terrain type                       on-screen symbol
                floor                              .
@@ -35,13 +38,7 @@
                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. Some items
-aid in exploration, e.g., a ring of searching improves the speed
-of finding hidden doors by heroes and monsters. The higher the ability
-bonus displayed for this and other items, the more effective it is.
-Only the best item carried in a hero's or monster's inventory counts.
-You can throw the rest away, but beware that your adversaries may pick it up
-and use it against your party.
+during a single game, the level layout is the same.
 
 
 Keys
@@ -57,71 +54,77 @@
                1 2 3     b j n
 
 SHIFT (or CTRL) and a movement key make the selected hero run in the indicated
-direction, until anything of interest is spotted. '5' and '.' use a turn
-to brace for combat, which gives a chance to block blows next turn.
+direction, until anything of interest is spotted. The '5' and '.' keys take
+a turn to brace for combat, which gives a chance to block blows next turn.
 Melee, searching for secret doors and opening closed doors can be done
 by bumping into a monster, a wall and a door, respectively.
 
 Below are the default keys for major commands. The last four commands
 are specialized versions of 'a' and 'p', offering narrower default item choice.
-The commands that take player time are marked with a *.
-
-               key     command
-               <       ascend a level*
-               >       descend a level*
-               ?       display help
-               R       restart game*
-               S       save game
-               X       save and exit*
-               a       apply a consumable*
-               c       close a door*
-               d       drop an object*
-               g       get an object*
-               i       display inventory
-               p       project a projectile*
-               q       quaff a drink*
-               r       read a comm*
-               t       throw a missile*
-               z       zap a mechanism*
+The commands that take player time are marked with a star.
 
-To make a ranged attack, you need to set your target first, using
-targeting mode. Note that the target, for the few commands that require any,
-is indicated by the targeting cursor. The origin of a command
---- the  hero that performs it --- is unaffected by targeting. For example,
-not the targeted door, but one adjacent to the selected hero is closed by him.
+               key       command
+               <         ascend a level*
+               >         descend a level*
+               ?         display help
+               I         display inventory
+               a         apply a consumable*
+               c         close a door*
+               d         drop an object*
+               g         get an object*
+               o         open a door*
+               p         project a projectile*
+               q         quaff a drink*
+               r         read a tablet*
+               t         throw a missile*
+               z         zap a mechanism*
 
+To make a ranged attack, you need to set your target first,
+using targeting mode ('*' and '/' keys). The target, for the few
+commands that require any, is indicated by the targeting cursor.
 To avoid confusion, commands that take time are blocked when targeting
-at a remote level (when the cursor is on a different level
+at a remote level (when the cursor is displayed on a different level
 than the selected hero). The targeting commands and all the less used
 commands are listed below. None of them takes hero time.
 
                key       command
-               ESC       cancel action
+               ESC       cancel action or bring up the Main Menu
                RET       accept choice
-               SPACE     clear messages
-               TAB       cycle among heroes on level
+               TAB       cycle among heroes on the level
                SHIFT-TAB cycle among heroes in the dungeon
                *         target monster
                +         swerve targeting line
                -         unswerve targeting line
-               /         target location
-               D         dump current configuration
+               /         target position
                P         display previous messages
+               S         mark smell
+               T         mark suspect terrain
+               V         mark visible area
                [         target next shallower level
                ]         target next deeper level
                {         target 10 levels shallower
                }         target 10 levels deeper
-               0--9      select a hero anywhere in the spaceship
+               0--9      select a hero anywhere in the dungeon
 
-There are also some debug and cheat keys, all entered with the CTRL
-key modifier. Use at your own peril!
+Commands for saving the current game, starting a new game, etc.,
+are listed in the Main Menu, brough up by the ESC key.
+Some of the game modes are multiplayer or feature multiple computer
+players (allied or not). The setup of the modes can be modified
+via a configuration file.
 
-               key     command
-               CTRL-o  toggle "omniscience"
-               CTRL-i  inform about level meta-data
-               CTRL-r  rotate vision modes (effective next turn)
+               key       command
+               CTRL-s    save game
+               CTRL-x    save and exit
+               CTRL-r    new campaign game
+               CTRL-k    new skirmish game
+               CTRL-p    new PvP game
+               CTRL-o    new Coop game
+               CTRL-e    new defense game
 
+There are also some debug and cheat options that can be specified
+on the command line when starting the game server. Use at your own peril!
 
+
 Monsters
 --------
 
@@ -136,8 +139,8 @@
 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 another (with the Shift key) do not inflict damage, but change places.
+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.
 This gives the opponent a free blow, but can improve the tactical situation
 or aid escape.
 
@@ -153,7 +156,8 @@
 On Winning and Dying
 --------------------
 
-You win the game if you escape the spaceship alive. Your score is
+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).
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-Allure of the Stars
+Allure of the Stars [![Build Status](https://secure.travis-ci.org/Mikolaj/Allure.png)](http://travis-ci.org/Mikolaj/Allure)
 ===================
 
 This is an alpha release of Allure of the Stars,
@@ -26,8 +26,8 @@
 Compatibility notes
 -------------------
 
-The current code was tested with GHC 7.6.1, but probably works with GHC > 7.2.
-A [few tweaks] [7] are needed to compile with 7.0.
+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).
 
 If you are using the curses or vty frontends,
 numerical keypad may not work correctly depending on the versions
@@ -51,7 +51,7 @@
 Copyright
 ---------
 
-Copyright (c) 2008--2011 Andres Loeh, 2010--2012 Mikolaj Konarski
+Copyright (c) 2008--2011 Andres Loeh, 2010--2013 Mikolaj Konarski
 
 Allure of the Stars is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
@@ -75,4 +75,3 @@
 [4]: http://hackage.haskell.org/package/Allure
 [5]: http://github.com/kosmikus/LambdaHack
 [6]: https://github.com/Mikolaj/Allure/wiki
-[7]: https://github.com/Mikolaj/Allure/commit/3d0aa5bef7a0ef39e7611d4e12229224f4cead75
diff --git a/config.rules.bot b/config.rules.bot
deleted file mode 100644
--- a/config.rules.bot
+++ /dev/null
@@ -1,14 +0,0 @@
-; Copyright (c) 2008--2011 Andres Loeh, 2010--2012 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.
-;
-; Overriding config file options to help the bot.
-; DumbBot 42 20000000 | Allure > /tmp/log
-
-[dungeon]
-depth: 100
-
-[heroes]
-baseHP: 999999
-extraHeroes: 1
diff --git a/config.rules.default b/config.rules.default
--- a/config.rules.default
+++ b/config.rules.default
@@ -9,25 +9,45 @@
 ; ; ~/.LambdaHack/config.rules.ini (or a similar path, depending on the OS).
 ; ; Warning: options are case-sensitive and only ';' for comments is permitted.
 
-; [dungeon]
-; depth: 12
-
 ; [caves]
+; ; Cave "dng" means a random choice from caves that can randomly appear;
+; ; this is the default and the lack of the Escape feature is the default.
 ; ; Fixed caves for the first levels, then randomly picked caves.
-; LambdaCave_1: caveRogue
-; LambdaCave_12: caveNoise
+; campaign: [(1, ("caveRogue", False)), (12, ("caveNoise", True))]
+; combat: [(3, ("caveCombat", False))]
+; defense: [(1, ("dng", False)), (6, ("caveEmpty", True))]
 
 ; [engine]
+; ;dungeonRandomGenerator: 42
+; firstDeathEnds: False
 ; fovMode: Digital 12
 ; ;fovMode: Permissive
 ; ;fovMode: Shadow
 ; ;startingRandomGenerator: 42
-; ;dungeonRandomGenerator: 42
-; smellTimeout: 100
+; saveBkpClips: 100
 
-; [heroes]
-; baseHP: 50
-; extraHeroes: 2
-; firstDeathEnds: False
-; ; faction "playable" means a random choice
-; faction: hero
+; [file]
+; ; Names of various game files. They reside in ~/.LambdaHack or similar.
+; scoresFile: scores
+
+; [heroName]
+; HeroName_0: Haskell Alvin
+; HeroName_1: Alonzo Barkley
+; HeroName_2: Ines Galenti
+; HeroName_3: Ernst Abraham
+; HeroName_4: Samuel Saunders
+; HeroName_5: Roger Robin
+
+; [players]
+; campaign: Players { playersHuman = [Player {playerName = "Spaceship Crew", playerKind = "hero", playerInitial = 1, playerEntry = LevelId 1}], playersComputer = [Player {playerName = "Alien Hierarchy", playerKind = "alien", playerInitial = 3, playerEntry = LevelId 5}, Player {playerName = "Animal Kingdom", playerKind = "animal", playerInitial = 3, playerEntry = LevelId 3}, Player {playerName = "Robotic Anarchy", playerKind = "robot", playerInitial = 3, playerEntry = LevelId 4}], playersEnemy = [("Spaceship Crew", "Alien Hierarchy"), ("Spaceship Crew", "Animal Kingdom"), ("Spaceship Crew", "Robotic Anarchy"), ("Animal Kingdom", "Alien Hierarchy"), ("Robotic Anarchy", "Alien Hierarchy"), ("Robotic Anarchy", "Animal Kingdom")], playersAlly = [] }
+; skirmish: Players { playersHuman = [Player {playerName = "White", playerKind = "hero", playerInitial = 3, playerEntry = LevelId 1}], playersComputer = [Player {playerName = "Green", playerKind = "hero", playerInitial = 3, playerEntry = LevelId 1}, Player {playerName = "Horror Den", playerKind = "horror", playerInitial = 0, playerEntry = LevelId 1}], playersEnemy = [("White", "Green"), ("White", "Horror Den"), ("Green", "Horror Den")], playersAlly = [] }
+; PvP: Players { playersHuman = [Player {playerName = "Red", playerKind = "hero", playerInitial = 3, playerEntry = LevelId 1}, Player {playerName = "Blue", playerKind = "hero", playerInitial = 3, playerEntry = LevelId 1}], playersComputer = [Player {playerName = "Horror Den", playerKind = "horror", playerInitial = 0, playerEntry = LevelId 1}], playersEnemy = [("Red", "Blue"), ("Red", "Horror Den"), ("Blue", "Horror Den")], playersAlly = [] }
+; Coop: Players { playersHuman = [Player {playerName = "Coral", playerKind = "hero", playerInitial = 1, playerEntry = LevelId 1}, Player {playerName = "Amber", playerKind = "hero", playerInitial = 1, playerEntry = LevelId 1}], playersComputer = [Player {playerName = "Alien Hierarchy", playerKind = "alien", playerInitial = 3, playerEntry = LevelId 5}, Player {playerName = "Animal Kingdom", playerKind = "animal", playerInitial = 3, playerEntry = LevelId 3}, Player {playerName = "Robotic Anarchy", playerKind = "robot", playerInitial = 3, playerEntry = LevelId 4}], playersEnemy = [("Coral", "Alien Hierarchy"), ("Coral", "Animal Kingdom"), ("Coral", "Robotic Anarchy"), ("Amber", "Alien Hierarchy"), ("Amber", "Animal Kingdom"), ("Amber", "Robotic Anarchy"), ("Animal Kingdom", "Alien Hierarchy"), ("Robotic Anarchy", "Alien Hierarchy"), ("Robotic Anarchy", "Animal Kingdom")], playersAlly = [("Coral", "Amber")] }
+; defense: Players { playersHuman = [Player {playerName = "Alien Hierarchy", playerKind = "alien", playerInitial = 1, playerEntry = LevelId 1}], playersComputer = [Player {playerName = "Green", playerKind = "hero", playerInitial = 1, playerEntry = LevelId 1}, Player {playerName = "Yellow", playerKind = "hero", playerInitial = 2, playerEntry = LevelId 1}, Player {playerName = "Cyan", playerKind = "hero", playerInitial = 3, playerEntry = LevelId 1}, Player {playerName = "Animal Kingdom", playerKind = "animal", playerInitial = 0, playerEntry = LevelId 1}, Player {playerName = "Robotic Anarchy", playerKind = "robot", playerInitial = 0, playerEntry = LevelId 1}], playersEnemy = [("Green", "Alien Hierarchy"), ("Green", "Animal Kingdom"), ("Green", "Robotic Anarchy"), ("Yellow", "Alien Hierarchy"), ("Yellow", "Animal Kingdom"), ("Yellow", "Robotic Anarchy"), ("Cyan", "Alien Hierarchy"), ("Cyan", "Animal Kingdom"), ("Cyan", "Robotic Anarchy"), ("Animal Kingdom", "Alien Hierarchy"), ("Robotic Anarchy", "Alien Hierarchy"), ("Robotic Anarchy", "Animal Kingdom")], playersAlly = [("Green", "Yellow"), ("Green", "Cyan"), ("Yellow", "Cyan")] }
+
+; [scenario]
+; campaign: Scenario { scenarioPlayers = "campaign", scenarioDungeon = "campaign" }
+; skirmish: Scenario { scenarioPlayers = "skirmish", scenarioDungeon = "combat" }
+; PvP: Scenario { scenarioPlayers = "PvP", scenarioDungeon = "combat" }
+; Coop: Scenario { scenarioPlayers = "Coop", scenarioDungeon = "campaign" }
+; defense: Scenario { scenarioPlayers = "defense", scenarioDungeon = "defense" }
diff --git a/config.ui.default b/config.ui.default
--- a/config.ui.default
+++ b/config.ui.default
@@ -12,70 +12,48 @@
 ; [commands]
 ; ; All commands are defined here, except movement, hero selection and debug.
 ; ;
-; ; Interaction with the dungeon.
-; c: TriggerDir { verb = "close", object = "door", feature = Closable }
-; less:    TriggerTile { verb = "ascend", object = "level", feature = Cause Ascend }
-; greater: TriggerTile { verb = "descend", object = "level", feature = Cause Descend }
+; KP_Begin: Wait
+; g: Pickup
+; d: Drop
+; p: Project [ApplyItem {verb = "project", object = "missile", symbol = ' '}, ApplyItem {verb = "throw", object = "projectile", symbol = '}'}, ApplyItem {verb = "throw", object = "projectile", symbol = '{'}, ApplyItem {verb = "zap", object = "mechanism", symbol = '-'}]
+; t: Project [ApplyItem {verb = "throw", object = "projectile", symbol = '}'}, ApplyItem {verb = "throw", object = "projectile", symbol = '{'}]
+; z: Project [ApplyItem {verb = "zap", object = "mechanism", 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 = '?'}]
+; q: Apply [ApplyItem {verb = "quaff", object = "drink", symbol = '!'}]
+; r: Apply [ApplyItem {verb = "read", object = "tablet", symbol = '?'}]
+; c: TriggerDir [BumpFeature {verb = "close", object = "door", feature = Closable}]
+; less: TriggerTile [BumpFeature {verb = "ascend", object = "level", feature = Cause (Ascend 1)}, BumpFeature {verb = "exit", object = "spaceship", feature = Cause Escape}]
+; greater: TriggerTile [BumpFeature {verb = "descend", object = "level", feature = Cause (Descend 1)}, BumpFeature {verb = "exit", object = "spaceship", feature = Cause Escape}]
+; CTRL-r: GameRestart "campaign"
+; ; TODO: pick game modes via arrow keys, not special keys
+; CTRL-k: GameRestart "skirmish"
+; CTRL-p: GameRestart "PvP"
+; CTRL-o: GameRestart "Coop"
+; CTRL-e: GameRestart "defense"
+; CTRL-x: GameExit
+; CTRL-s: GameSave
+; CTRL-d: CfgDump
+; Tab: MemberCycle
+; ISO_Left_Tab: MemberBack
+; I: Inventory
+; slash: TgtFloor
+; asterisk: TgtEnemy
 ; bracketleft:  TgtAscend 1
 ; bracketright: TgtAscend (-1)
 ; braceleft:    TgtAscend 10
 ; braceright:   TgtAscend (-10)
-; slash: TgtFloor
-; asterisk: TgtEnemy
 ; plus: EpsIncr True
 ; minus: EpsIncr False
-; Tab: HeroCycle
-; ISO_Left_Tab: HeroBack
-; ;
-; ; Items.
-; g: Pickup
-; d: Drop
-; i: Inventory
-; a: Apply { verb = "apply", object = "consumable", syms = "!?,_~" }
-; p: Project { verb = "project", object = "missile", syms = "-}{" }
-; q: Apply { verb = "quaff", object = "drink", syms = "!" }
-; r: Apply { verb = "read", object = "tablet", syms = "?" }
-; z: Project { verb = "zap", object = "mechanism", syms = "-" }
-; t: Project { verb = "throw", object = "projectile", syms = "}{" }
-; ;
-; ; Saving, exiting and restarting the game.
-; X: GameExit
-; R: GameRestart
-; S: GameSave
-; ;
-; ; Information for the player.
-; P: History
-; question: Help
-; D: CfgDump
-; ;
-; ; General.
-; KP_Begin: Wait
 ; Escape: Cancel
 ; Return: Accept
 ; space: Clear
-
-; [files]
-; ; Names of various game files. They reside in ~/.LambdaHack or similar.
-; scoresFile: scores
-; saveFile: save
-; diaryFile : diary
-
-; [heroNames]
-; HeroName_0: you
-; HeroName_1: Haskell Alvin
-; HeroName_2: Alonzo Barkley
-; HeroName_3: Ernst Abraham
-; HeroName_4: Samuel Saunders
-; HeroName_5: Roger Robin
+; P: History
+; V: MarkVision
+; S: MarkSmell
+; T: MarkSuspect
+; question: Help
 
 ; [macros]
-; ; This sections can be empty, but has to be there.
-; ; TODO: the following does not work yet:
-; ;; throw a dart at the closest monster
-; ;t: asterisk Return t Return
-; ; TODO: in gtk it could be implemented via unGetChan,
-; ; unless we prefer an explicit command queue, with flushing, etc.
-; ;
 ; ; Handy with Vi keys:
 ; comma: g
 ; period: KP_Begin
diff --git a/scores b/scores
Binary files a/scores and b/scores differ
diff --git a/src/Content/ActorKind.hs b/src/Content/ActorKind.hs
--- a/src/Content/ActorKind.hs
+++ b/src/Content/ActorKind.hs
@@ -7,15 +7,15 @@
 -- | Monsters and heroes for Allure of the Stars.
 module Content.ActorKind ( cdefs ) where
 
-import Game.LambdaHack.Ability
-import Game.LambdaHack.Color
-import Game.LambdaHack.CDefs
+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
-import Game.LambdaHack.Random
-import Game.LambdaHack.Time
 
-cdefs :: CDefs ActorKind
-cdefs = CDefs
+cdefs :: ContentDef ActorKind
+cdefs = ContentDef
   { getSymbol = asymbol
   , getName = aname
   , getFreq = afreq
@@ -28,13 +28,13 @@
 hero = ActorKind
   { asymbol = '@'
   , aname   = "hero"
-  , afreq   = [("hero", 1)]  -- Does not appear randomly in the dungeon.
-  , acolor  = BrWhite  -- Heroes white, monsters colorful.
-  , ahp     = RollDice 60 1
+  , afreq   = [("hero", 1)]
+  , acolor  = BrWhite  -- modified if many hero factions
+  , ahp     = RollDice 50 1
   , aspeed  = toSpeed 2
   , asight  = True
   , asmell  = False
-  , aiq     = 13  -- Can see hidden doors, when he is under alien control.
+  , aiq     = 16
   , aregen  = 500
   , acanDo  = [minBound..maxBound]
   }
@@ -56,7 +56,7 @@
 eye = ActorKind
   { asymbol = 'r'
   , aname   = "deranged household robot"
-  , afreq   = [("robot", 60), ("summon", 50)]
+  , afreq   = [("robot", 60), ("horror", 60)]
   , acolor  = BrYellow
   , ahp     = RollDice 7 4
   , aspeed  = toSpeed 2
@@ -69,7 +69,7 @@
 fastEye = ActorKind
   { asymbol = 'm'
   , aname   = "deformed monkey"
-  , afreq   = [("animal", 15)]
+  , afreq   = [("animal", 15), ("horror", 15)]
   , acolor  = BrMagenta
   , ahp     = RollDice 1 4
   , aspeed  = toSpeed 4
@@ -82,7 +82,7 @@
 nose = ActorKind
   { asymbol = 'h'
   , aname   = "tentacled horror"
-  , afreq   = [("alien", 20), ("summon", 100)]
+  , afreq   = [("alien", 20), ("horror", 20)]
   , acolor  = Green
   , ahp     = RollDice 17 2
   , aspeed  = toSpeed 1.8
diff --git a/src/Content/CaveKind.hs b/src/Content/CaveKind.hs
--- a/src/Content/CaveKind.hs
+++ b/src/Content/CaveKind.hs
@@ -9,21 +9,21 @@
 
 import Data.Ratio
 
-import Game.LambdaHack.CDefs
-import Game.LambdaHack.Misc
-import Game.LambdaHack.Random as Random
+import Game.LambdaHack.Common.ContentDef
+import Game.LambdaHack.Common.Misc
+import Game.LambdaHack.Common.Random as Random
 import Game.LambdaHack.Content.CaveKind
 
-cdefs :: CDefs CaveKind
-cdefs = CDefs
+cdefs :: ContentDef CaveKind
+cdefs = ContentDef
   { getSymbol = csymbol
   , getName = cname
   , getFreq = cfreq
   , validate = cvalidate
   , content =
-      [rogue, arena, empty, noise]
+      [rogue, arena, empty, noise, combat]
   }
-rogue,        arena, empty, noise :: CaveKind
+rogue,        arena, empty, noise, combat :: CaveKind
 
 rogue = CaveKind
   { csymbol       = '$'
@@ -40,14 +40,13 @@
   , cminStairDist = 30
   , cdoorChance   = 1%2
   , copenChance   = 1%10
-  , chiddenChance = 1%5
+  , chidden       = 8
   , citemNum      = RollDice 7 2
-  , cdefaultTile    = "fillerWall"
+  , cdefTile        = "fillerWall"
   , ccorridorTile   = "darkCorridor"
   , cfillerTile     = "fillerWall"
   , cdarkLegendTile = "darkLegend"
   , clitLegendTile  = "litLegend"
-  , chiddenTile     = "hidden"
   }
 arena = rogue
   { csymbol       = 'A'
@@ -58,8 +57,9 @@
   , cdarkChance   = (RollDice 1 80, RollDice 1 60)
   , cvoidChance   = 1%3
   , cnonVoidMin   = 2
+  , chidden       = 9
   , citemNum      = RollDice 4 2  -- few rooms
-  , cdefaultTile  = "floorArenaLit"
+  , cdefTile      = "floorArenaLit"
   , ccorridorTile = "path"
   }
 empty = rogue
@@ -73,8 +73,9 @@
   , cvoidChance   = 3%4
   , cnonVoidMin   = 1
   , cminStairDist = 50
+  , chidden       = 10
   , citemNum      = RollDice 8 2  -- whole floor strewn with treasure
-  , cdefaultTile  = "floorRoomLit"
+  , cdefTile      = "floorRoomLit"
   , ccorridorTile = "floorRoomLit"
   }
 noise = rogue
@@ -86,7 +87,22 @@
   , cdarkChance   = (RollDice 1 80, RollDice 1 40)
   , cvoidChance   = 0
   , cnonVoidMin   = 0
+  , chidden       = 6
   , citemNum      = RollDice 4 2  -- few rooms
-  , cdefaultTile  = "noiseSet"
+  , cdefTile      = "noiseSet"
+  , ccorridorTile = "path"
+  }
+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
+  , citemNum      = RollDice 12 2
+  , cdefTile      = "combatSet"
   , ccorridorTile = "path"
   }
diff --git a/src/Content/FactionKind.hs b/src/Content/FactionKind.hs
--- a/src/Content/FactionKind.hs
+++ b/src/Content/FactionKind.hs
@@ -4,61 +4,62 @@
 -- and is released under the terms of the GNU Affero General Public License.
 -- For license and copyright information, see the file LICENSE.
 --
--- | Game factions (heroes, enemies, NPCs, etc.) for Allure of the Stars.
+-- | The type of kinds of game factions (heroes, enemies, NPCs, etc.)
+-- for LambdaHack.
 module Content.FactionKind ( cdefs ) where
 
-import Game.LambdaHack.CDefs
+import Game.LambdaHack.Common.ContentDef
 import Game.LambdaHack.Content.FactionKind
 
-cdefs :: CDefs FactionKind
-cdefs = CDefs
+cdefs :: ContentDef FactionKind
+cdefs = ContentDef
   { getSymbol = fsymbol
   , getName = fname
   , getFreq = ffreq
   , validate = fvalidate
   , content =
-      [hero, alien, animal, robot]
+      [hero, alien, animal, robot, horror]
   }
-hero,        alien, animal, robot :: FactionKind
+hero,        alien, animal, robot, horror :: FactionKind
 
 hero = FactionKind
-  { fsymbol     = '@'
-  , fname       = "hero"
-  , ffreq       = [("hero", 1), ("playable", 50)]
-  , fAiSelected = "noAbility"  -- no AI, fully manual control
-  , fAiIdle     = "meleeAdjacent"
-  , fenemy      = ["alien"]
-  , fally       = []
+  { fsymbol   = '@'
+  , fname     = "hero"
+  , ffreq     = [("hero", 1)]
+  , fAiLeader = "fullAbility"
+  , fAiMember = "meleeAdjacent"
   }
 
 -- Includes alien-operated robots, alien-conditioned animals and hybrids.
 alien = FactionKind
   { fsymbol     = 'a'
   , fname       = "alien"
-  , ffreq       = [("alien", 1), ("playable", 50), ("spawn", 20)]
-  , fAiSelected = "fullAbility"
-  , fAiIdle     = "fullAbility"
-  , fenemy      = ["hero"]
-  , fally       = []
+  , ffreq       = [("alien", 1), ("spawn", 20), ("summon", 20)]
+  , fAiLeader   = "fullAbility"
+  , fAiMember   = "fullAbility"
   }
 
 animal = FactionKind
   { fsymbol     = 'd'
   , fname       = "animal"
-  , ffreq       = [("animal", 1), ("spawn", 50)]
-  , fAiSelected = "fullAbility"
-  , fAiIdle     = "fullAbility"
-  , fenemy      = ["hero", "alien"]  -- animals hunt external intruders
-  , fally       = []
+  , ffreq       = [("animal", 1), ("spawn", 50), ("summon", 50)]
+  , fAiLeader   = "animalAbility"
+  , fAiMember   = "animalAbility"
   }
 
 -- Autonomous robots.
 robot = FactionKind
   { fsymbol     = 'r'
   , fname       = "robot"
-  , ffreq       = [("robot", 1), ("spawn", 30)]
-  , fAiSelected = "fullAbility"
-  , fAiIdle     = "fullAbility"
-  , fenemy      = ["hero", "alien", "animal", "robot"]  -- hunt all that moves
-  , fally       = []
+  , ffreq       = [("robot", 1), ("spawn", 10), ("summon", 10)]
+  , fAiLeader   = "robotAbility"
+  , fAiMember   = "robotAbility"
+  }
+
+horror = FactionKind
+  { fsymbol   = 'h'
+  , fname     = "horror"
+  , ffreq     = [("horror", 1), ("summon", 50)]
+  , fAiLeader = "fullAbility"
+  , fAiMember = "fullAbility"
   }
diff --git a/src/Content/ItemKind.hs b/src/Content/ItemKind.hs
--- a/src/Content/ItemKind.hs
+++ b/src/Content/ItemKind.hs
@@ -9,25 +9,25 @@
 
 import qualified Data.List as L
 
-import Game.LambdaHack.Color
-import Game.LambdaHack.CDefs
-import Game.LambdaHack.Effect
-import Game.LambdaHack.Flavour
-import Game.LambdaHack.Random
+import Game.LambdaHack.Common.Color
+import Game.LambdaHack.Common.ContentDef
+import Game.LambdaHack.Common.Effect
+import Game.LambdaHack.Common.Flavour
+import Game.LambdaHack.Common.Random
 import Game.LambdaHack.Content.ItemKind
 
-cdefs :: CDefs ItemKind
-cdefs = CDefs
+cdefs :: ContentDef ItemKind
+cdefs = ContentDef
   { getSymbol = isymbol
   , getName = iname
   , getFreq = ifreq
   , validate = ivalidate
   , content =
-      [necklace, dart, gem1, gem2, gem3, currency, javelin, kitchenKnife, potion1, potion2, potion3, ring, scroll1, scroll2, scroll3, sword, wand, fist, foot, tentacle, weight]
+      [necklace, dart, gem1, gem2, gem3, currency, javelin, kitchenKnife, potion1, potion2, potion3, ring, scroll1, scroll2, scroll3, sword, wand1, wand2, fist, foot, tentacle]
   }
-necklace,        dart, gem1, gem2, gem3, currency, javelin, kitchenKnife, potion1, potion2, potion3, ring, scroll1, scroll2, scroll3, sword, wand, fist, foot, tentacle, weight :: ItemKind
+necklace,        dart, gem1, gem2, gem3, currency, javelin, kitchenKnife, potion1, potion2, potion3, ring, scroll1, scroll2, scroll3, sword, wand1, wand2, fist, foot, tentacle :: ItemKind
 
-gem, potion, scroll :: ItemKind  -- generic templates
+gem, potion, scroll, wand :: ItemKind  -- generic templates
 
 -- rollDeep (aDb, xDy) = rollDice aDb + lvl * rollDice xDy / depth
 
@@ -59,9 +59,8 @@
   , iname    = "necklace"
   , ifreq    = [("dng", 6)]
   , iflavour = zipFancy [BrGreen]
-  , ieffect  = Regeneration
+  , ieffect  = Regeneration (RollDice 2 3, RollDice 1 10)
   , icount   = intToDeep 1
-  , ipower   = (RollDice 2 3, RollDice 1 10)
   , iverbApply   = "tear down"
   , iverbProject = "cast"
   , iweight  = 30
@@ -72,22 +71,20 @@
   , iname    = "billiard ball"
   , ifreq    = [("dng", 30)]
   , iflavour = zipPlain [BrWhite]
-  , ieffect  = Wound (RollDice 1 1)
+  , ieffect  = Hurt (RollDice 1 1) (RollDice 1 2, RollDice 1 2)
   , icount   = (RollDice 3 3, RollDice 0 0)
-  , ipower   = intToDeep 0
   , iverbApply   = "splinter"
   , iverbProject = "hurl"
   , iweight  = 50
   , itoThrow = 0  -- a cheap dart
   }
 gem = ItemKind
-  { isymbol  = '$'
+  { isymbol  = '*'
   , iname    = "precious gem"
   , ifreq    = [("dng", 20)]       -- x3, but rare on shallow levels
   , iflavour = zipPlain $ L.delete BrYellow brightCol  -- natural, so not fancy
   , ieffect  = NoEffect
   , icount   = intToDeep 0
-  , ipower   = intToDeep 0
   , iverbApply   = "crush"
   , iverbProject = "toss"
   , iweight  = 50
@@ -105,11 +102,10 @@
 currency = ItemKind
   { isymbol  = '$'
   , iname    = "gold grain"
-  , ifreq    = [("dng", 80), ("currency", 1)]
+  , ifreq    = [("dng", 50), ("currency", 1)]
   , iflavour = zipPlain [BrYellow]
   , ieffect  = NoEffect
   , icount   = (RollDice 0 0, RollDice 10 10)
-  , ipower   = intToDeep 0
   , iverbApply   = "smear"
   , iverbProject = "blow away"
   , iweight  = 1
@@ -120,9 +116,8 @@
   , iname    = "javelin"
   , ifreq    = [("dng", 30)]
   , iflavour = zipPlain [Brown]
-  , ieffect  = Wound (RollDice 1 2)
+  , ieffect  = Hurt (RollDice 1 2) (RollDice 1 2, RollDice 2 2)
   , icount   = (RollDice 0 0, RollDice 1 1)
-  , ipower   = (RollDice 1 1, RollDice 2 2)
   , iverbApply   = "break up"
   , iverbProject = "hurl"
   , iweight  = 2000
@@ -133,9 +128,8 @@
   , iname    = "kitchen knife"
   , ifreq    = [("dng", 30)]
   , iflavour = zipPlain [BrCyan]
-  , ieffect  = Wound (RollDice 1 1)
+  , ieffect  = Hurt (RollDice 1 1) (RollDice 0 0, RollDice 1 2)
   , icount   = (RollDice 0 0, RollDice 1 2)
-  , ipower   = (RollDice 0 0, RollDice 1 2)
   , iverbApply   = "bend"
   , iverbProject = "throw"
   , iweight  = 200
@@ -148,7 +142,6 @@
   , iflavour = zipFancy stdCol
   , ieffect  = NoEffect
   , icount   = intToDeep 1
-  , ipower   = intToDeep 0
   , iverbApply   = "gulp down"
   , iverbProject = "lob"
   , iweight  = 200
@@ -159,22 +152,19 @@
   , ieffect  = ApplyPerfume
   }
 potion2 = potion
-  { ieffect  = Heal
-  , ipower   = (RollDice 5 1, RollDice 0 0)
+  { ieffect  = Heal 5
   }
 potion3 = potion
   { ifreq    = [("dng", 5)]
-  , ieffect  = Wound (RollDice 0 0)
-  , ipower   = (RollDice 5 1, RollDice 0 0)
+  , ieffect  = Heal (-5)
   }
 ring = ItemKind
   { isymbol  = '='
   , iname    = "ring"
-  , ifreq    = [("dng", 10)]
+  , ifreq    = []  -- [("dng", 10)]  -- TODO: make it useful
   , iflavour = zipPlain [White]
-  , ieffect  = Searching
+  , ieffect  = Searching (RollDice 1 6, RollDice 3 2)
   , icount   = intToDeep 1
-  , ipower   = (RollDice 1 6, RollDice 3 2)
   , iverbApply   = "squeeze down"
   , iverbProject = "toss"
   , iweight  = 15
@@ -187,29 +177,27 @@
   , iflavour = zipFancy darkCol  -- arcane and old
   , ieffect  = NoEffect
   , icount   = intToDeep 1
-  , ipower   = intToDeep 0
   , iverbApply   = "dial"
   , iverbProject = "lob"
   , iweight  = 700
   , itoThrow = -25  -- bad grip
   }
 scroll1 = scroll
-  { ieffect  = SummonFriend
+  { ieffect  = CallFriend 1
   }
 scroll2 = scroll
-  { ieffect  = SummonEnemy
+  { ieffect  = Summon 1
   }
 scroll3 = scroll
-  { ieffect  = Descend
+  { ieffect  = Descend 1
   }
 sword = ItemKind
   { isymbol  = '/'
   , iname    = "sharpened pipe"
-  , ifreq    = [("dng", 60)]
+  , ifreq    = [("dng", 40)]
   , iflavour = zipPlain [Cyan]
-  , ieffect  = Wound (RollDice 3 1)
+  , ieffect  = Hurt (RollDice 3 1) (RollDice 1 2, RollDice 4 2)
   , icount   = intToDeep 1
-  , ipower   = (RollDice 1 2, RollDice 4 2)
   , iverbApply   = "hit"
   , iverbProject = "heave"
   , iweight  = 3000
@@ -219,42 +207,42 @@
   { isymbol  = '-'
   , iname    = "injector"
   , ifreq    = [("dng", 15)]
-  , iflavour = zipFancy [BrRed]
-  , ieffect  = Dominate
+  , iflavour = zipFancy brightCol
+  , ieffect  = NoEffect
   , icount   = intToDeep 1
-  , ipower   = intToDeep 0
   , iverbApply   = "snap"
   , iverbProject = "zap"
   , iweight  = 300
   , itoThrow = 25  -- jet
   }
+wand1 = wand
+  { ieffect  = Dominate
+  }
+wand2 = wand
+  { ifreq    = [("dng", 3)]
+  , ieffect  = Heal (-25)
+  }
 fist = sword
   { isymbol  = '@'
   , iname    = "fist"
-  , ifreq    = [("unarmed", 100)]
+  , ifreq    = [("hth", 1), ("unarmed", 100)]
+  , ieffect  = Hurt (RollDice 3 1) (intToDeep 0)
   , iverbApply   = "punch"
   , iverbProject = "ERROR, please report: iverbProject fist"
   }
 foot = sword
   { isymbol  = '@'
   , iname    = "foot"
-  , ifreq    = [("unarmed", 50)]
+  , ifreq    = [("hth", 1), ("unarmed", 50)]
+  , ieffect  = Hurt (RollDice 3 1) (intToDeep 0)
   , iverbApply   = "kick"
   , iverbProject = "ERROR, please report: iverbProject foot"
   }
 tentacle = sword
   { isymbol  = 'S'
   , iname    = "tentacle"
-  , ifreq    = [("monstrous", 100)]
+  , ifreq    = [("hth", 1), ("monstrous", 100)]
+  , ieffect  = Hurt (RollDice 3 1) (intToDeep 0)
   , iverbApply   = "hit"
   , iverbProject = "ERROR, please report: iverbProject tentacle"
-  }
-weight = sword
-  { isymbol  = '@'
-  , iname    = "power jump"
-  , ifreq    = [("weight", 100)]
-  , ieffect  = Wound (RollDice 99 99)
-  , ipower   = (RollDice 1 99, RollDice 0 0)
-  , iverbApply   = "squash"
-  , iverbProject = "ERROR, please report: iverbProject weight"
   }
diff --git a/src/Content/PlaceKind.hs b/src/Content/PlaceKind.hs
--- a/src/Content/PlaceKind.hs
+++ b/src/Content/PlaceKind.hs
@@ -7,11 +7,11 @@
 -- | Rooms, halls and passages for Allure of the Stars.
 module Content.PlaceKind ( cdefs ) where
 
-import Game.LambdaHack.CDefs
+import Game.LambdaHack.Common.ContentDef
 import Game.LambdaHack.Content.PlaceKind
 
-cdefs :: CDefs PlaceKind
-cdefs = CDefs
+cdefs :: ContentDef PlaceKind
+cdefs = ContentDef
   { getSymbol = psymbol
   , getName = pname
   , getFreq = pfreq
diff --git a/src/Content/RuleKind.hs b/src/Content/RuleKind.hs
--- a/src/Content/RuleKind.hs
+++ b/src/Content/RuleKind.hs
@@ -1,5 +1,4 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE CPP, QuasiQuotes #-}
+{-# LANGUAGE CPP, OverloadedStrings, QuasiQuotes #-}
 -- Copyright (c) 2008--2011 Andres Loeh, 2010--2012 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.
@@ -11,14 +10,14 @@
 -- Cabal
 import qualified Paths_Allure as Self (getDataFileName, version)
 
+import Game.LambdaHack.Common.ContentDef
+import qualified Game.LambdaHack.Common.Feature as F
 import Game.LambdaHack.Content.RuleKind
 import Game.LambdaHack.Content.TileKind
-import qualified Game.LambdaHack.Feature as F
-import Game.LambdaHack.CDefs
 import Multiline
 
-cdefs :: CDefs RuleKind
-cdefs = CDefs
+cdefs :: ContentDef RuleKind
+cdefs = ContentDef
   { getSymbol = rsymbol
   , getName = rname
   , getFreq = rfreq
@@ -26,16 +25,16 @@
   , content =
       [standard]
   }
-standard :: RuleKind
 
+standard :: RuleKind
 standard = RuleKind
   { rsymbol        = 's'
   , rname          = "standard Allure of the Stars ruleset"
   , rfreq          = [("standard", 100)]
-  -- Check whether one location is accessible from another.
-  -- Precondition: the two locations are next to each other.
+  -- Check whether one position is accessible from another.
+  -- Precondition: the two positions are next to each other.
   -- TODO: in the future check flying for chasms, swimming for water, etc.
-  , raccessible    = \ _lxsize _sloc _src _tloc tgt ->
+  , raccessible    = \ _lxsize _spos _src _tpos tgt ->
       F.Walkable `elem` tfeature tgt
   , rtitle         = "Allure of the Stars"
   , rpathsDataFile = Self.getDataFileName
@@ -72,11 +71,8 @@
   , rmainMenuArt = [multiline|
 ----------------------------------------------------------------------------------
 |                                                                                |
-|                                                                                |
 |                      >> Allure of the Stars <<                                 |
 |                                                                                |
-|                                                                                |
-|                                                                                |
 |                      {{{{{{{{{{{{{{{{{{{{{{{{{                                 |
 |                                                                                |
 |                      {{{{{{{{{{{{{{{{{{{{{{{{{                                 |
@@ -87,10 +83,13 @@
 |                                                                                |
 |                      {{{{{{{{{{{{{{{{{{{{{{{{{                                 |
 |                                                                                |
-|                                                                                |
+|                      {{{{{{{{{{{{{{{{{{{{{{{{{                                 |
 |                                                                                |
+|                      {{{{{{{{{{{{{{{{{{{{{{{{{                                 |
 |                                                                                |
+|                      {{{{{{{{{{{{{{{{{{{{{{{{{                                 |
 |                                                                                |
+|                      {{{{{{{{{{{{{{{{{{{{{{{{{                                 |
 |                                                                                |
 |                                                                                |
 |                                                                                |
diff --git a/src/Content/StrategyKind.hs b/src/Content/StrategyKind.hs
--- a/src/Content/StrategyKind.hs
+++ b/src/Content/StrategyKind.hs
@@ -7,20 +7,22 @@
 -- | AI strategies for Allure of the Stars.
 module Content.StrategyKind ( cdefs ) where
 
-import Game.LambdaHack.Ability
-import Game.LambdaHack.CDefs
+import Data.List
+
+import Game.LambdaHack.Common.Ability
+import Game.LambdaHack.Common.ContentDef
 import Game.LambdaHack.Content.StrategyKind
 
-cdefs :: CDefs StrategyKind
-cdefs = CDefs
+cdefs :: ContentDef StrategyKind
+cdefs = ContentDef
   { getSymbol = ssymbol
   , getName = sname
   , getFreq = sfreq
   , validate = svalidate
   , content =
-      [noAbility, onlyFollowTrack, meleeAdjacent, meleeAndRanged, fullAbility]
+      [noAbility, onlyFollowTrack, meleeAdjacent, meleeAndRanged, animalAbility, robotAbility, fullAbility]
   }
-noAbility,        onlyFollowTrack, meleeAdjacent, meleeAndRanged, fullAbility :: StrategyKind
+noAbility,        onlyFollowTrack, meleeAdjacent, meleeAndRanged, animalAbility, robotAbility, fullAbility :: StrategyKind
 
 noAbility = StrategyKind  -- not even projectiles will fly
   { ssymbol    = '@'
@@ -40,14 +42,28 @@
   { ssymbol    = '@'
   , sname      = "meleeAdjacent"
   , sfreq      = [("meleeAdjacent", 1)]
-  , sabilities = [Melee, Track]
+  , sabilities = [Track, Melee]
   }
 
 meleeAndRanged = StrategyKind  -- melee and reaction fire
   { ssymbol    = '@'
   , sname      = "meleeAndRanged"
   , sfreq      = [("meleeAndRanged", 1)]
-  , sabilities = [Melee, Ranged, Track]
+  , sabilities = [Track, Melee, Ranged]
+  }
+
+animalAbility = StrategyKind
+  { ssymbol    = '@'
+  , sname      = "animalAbility"
+  , sfreq      = [("animalAbility", 1)]
+  , sabilities = [Track, Flee, Melee, Chase, Wander]
+  }
+
+robotAbility = StrategyKind
+  { ssymbol    = '@'
+  , sname      = "robotAbility"
+  , sfreq      = [("robotAbility", 1)]
+  , sabilities = delete Flee [minBound..maxBound]
   }
 
 fullAbility = StrategyKind
diff --git a/src/Content/TileKind.hs b/src/Content/TileKind.hs
--- a/src/Content/TileKind.hs
+++ b/src/Content/TileKind.hs
@@ -7,36 +7,57 @@
 -- | Terrain tiles for Allure of the Stars.
 module Content.TileKind ( cdefs ) where
 
-import Game.LambdaHack.Color
-import Game.LambdaHack.CDefs
-import qualified Game.LambdaHack.Effect as Effect
-import Game.LambdaHack.Feature
+import Game.LambdaHack.Common.Color
+import Game.LambdaHack.Common.ContentDef
+import qualified Game.LambdaHack.Common.Effect as Effect
+import Game.LambdaHack.Common.Feature
 import Game.LambdaHack.Content.TileKind
-import Game.LambdaHack.Random
 
-cdefs :: CDefs TileKind
-cdefs = CDefs
+cdefs :: ContentDef TileKind
+cdefs = ContentDef
   { getSymbol = tsymbol
   , getName = tname
   , getFreq = tfreq
   , validate = tvalidate
   , content =
-      [wall, doorHidden, doorClosed, doorOpen, pillar, stairsUp, stairsDown, unknown, floorCorridorLit, floorCorridorDark, floorRoomLit, floorRoomDark, floorRed, floorBlue, floorGreen]
+      [wall, hardRock, pillar, wallSuspect, doorClosed, doorOpen, stairsUp, stairsDown, escapeUp, unknown, floorCorridorLit, floorCorridorDark, floorItemLit, floorItemDark, floorActorItemLit, floorActorItemDark, floorRed, floorBlue, floorGreen]
   }
-wall,        doorHidden, doorClosed, doorOpen, pillar, stairsUp, stairsDown, unknown, floorCorridorLit, floorCorridorDark, floorRoomLit, floorRoomDark, floorRed, floorBlue, floorGreen :: TileKind
+wall,        hardRock, pillar, wallSuspect, doorClosed, doorOpen, stairsUp, stairsDown, escapeUp, unknown, floorCorridorLit, floorCorridorDark, floorItemLit, floorItemDark, floorActorItemLit, floorActorItemDark, floorRed, floorBlue, floorGreen :: TileKind
 
 wall = TileKind
   { tsymbol  = '#'
-  , tname    = "wall"
-  , tfreq    = [ ("litLegend", 100), ("darkLegend", 100)
-               , ("fillerWall", 1), ("noiseSet", 55) ]
+  , tname    = "granite wall"
+  , tfreq    = [ ("litLegend", 100), ("darkLegend", 100), ("fillerWall", 1)
+               , ("noiseSet", 55), ("combatSet", 5) ]
   , tcolor   = BrWhite
   , tcolor2  = defFG
+  , tfeature = [HiddenAs "suspect wall"]
+  }
+hardRock = TileKind
+  { tsymbol  = '#'
+  , tname    = "hard rock"
+  , tfreq    = [("hard rock", 1)]
+  , tcolor   = BrBlack
+  , tcolor2  = BrBlack
+  , tfeature = [Impenetrable]
+  }
+pillar = TileKind
+  { tsymbol  = 'O'
+  , tname    = "pillar"
+  , tfreq    = [("litLegend", 100), ("darkLegend", 100)]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
   , tfeature = []
   }
-doorHidden = wall
-  { tfreq    = [("hidden", 100)]
-  , tfeature = [Hidden, Secret (RollDice 7 2), ChangeTo "closed door"]
+wallSuspect = TileKind
+  { tsymbol  = '#'
+  , tname    = "moldy wall"
+  , tfreq    = [("suspect wall", 1)]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
+  , tfeature = [ Suspect
+               , ChangeTo "closed door"  -- never triggered, hack 47
+               ]
   }
 doorClosed = TileKind
   { tsymbol  = '+'
@@ -44,7 +65,10 @@
   , tfreq    = [("litLegend", 100), ("darkLegend", 100), ("closed door", 1)]
   , tcolor   = Brown
   , tcolor2  = BrBlack
-  , tfeature = [Exit, Openable, ChangeTo "open door"]
+  , tfeature = [ Exit, Openable
+               , ChangeTo "open door"
+               , HiddenAs "suspect wall"
+               ]
   }
 doorOpen = TileKind
   { tsymbol  = '\''
@@ -54,21 +78,14 @@
   , tcolor2  = BrBlack
   , tfeature = [Walkable, Clear, Exit, Closable, ChangeTo "closed door"]
   }
-pillar = TileKind
-  { tsymbol  = 'O'
-  , tname    = "pillar"
-  , tfreq    = [("litLegend", 100), ("darkLegend", 100)]
-  , tcolor   = BrWhite
-  , tcolor2  = defFG
-  , tfeature = []
-  }
 stairsUp = TileKind
   { tsymbol  = '<'
   , tname    = "staircase up"
   , tfreq    = [("litLegend", 100), ("darkLegend", 100)]
   , tcolor   = BrWhite
   , tcolor2  = defFG
-  , tfeature = [Walkable, Clear, Lit, Exit, Ascendable, Cause Effect.Ascend]
+  , tfeature = [ Walkable, Clear, Lit, Exit, Ascendable
+               , Cause $ Effect.Ascend 1 ]
   }
 stairsDown = TileKind
   { tsymbol  = '>'
@@ -76,8 +93,17 @@
   , tfreq    = [("litLegend", 100), ("darkLegend", 100)]
   , tcolor   = BrWhite
   , tcolor2  = defFG
-  , tfeature = [Walkable, Clear, Lit, Exit, Descendable, Cause Effect.Descend]
+  , tfeature = [ Walkable, Clear, Lit, Exit, Descendable
+               , Cause $ Effect.Descend 1 ]
   }
+escapeUp = TileKind
+  { tsymbol  = '<'
+  , tname    = "exit airlock up"
+  , tfreq    = [("litLegend", 100), ("darkLegend", 100)]
+  , tcolor   = BrYellow
+  , tcolor2  = BrYellow
+  , tfeature = [ Walkable, Clear, Lit, Exit, Cause Effect.Escape ]
+  }
 unknown = TileKind
   { tsymbol  = ' '
   , tname    = "unknown space"
@@ -89,7 +115,7 @@
 floorCorridorLit = TileKind
   { tsymbol  = '.'
   , tname    = "floor"
-  , tfreq    = [("noiseSet", 100), ("floorArenaLit", 1)]
+  , tfreq    = [("floorArenaLit", 1), ("noiseSet", 100)]
   , tcolor   = BrWhite
   , tcolor2  = defFG
   , tfeature = [Walkable, Clear, Lit]
@@ -100,13 +126,21 @@
   , tcolor2  = BrBlack
   , tfeature = [Walkable, Clear]
   }
-floorRoomLit = floorCorridorLit
+floorItemLit = floorCorridorLit
+  { tfreq    = [("combatSet", 100)]
+  , tfeature = CanItem : tfeature floorCorridorLit
+  }
+floorItemDark = floorCorridorDark
+  { tfreq    = []
+  , tfeature = CanItem : tfeature floorCorridorDark
+  }
+floorActorItemLit = floorItemLit
   { tfreq    = [("litLegend", 100), ("floorRoomLit", 1)]
-  , tfeature = Boring : tfeature floorCorridorLit
+  , tfeature = CanActor : tfeature floorItemLit
   }
-floorRoomDark = floorCorridorDark
+floorActorItemDark = floorItemDark
   { tfreq    = [("darkLegend", 100)]
-  , tfeature = Boring : tfeature floorCorridorDark
+  , tfeature = CanActor : tfeature floorItemDark
   }
 floorRed = floorCorridorLit
   { tname    = "emergency walkway"
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -1,3 +1,4 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
 -- Copyright (c) 2008--2011 Andres Loeh, 2010--2012 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.
@@ -8,7 +9,6 @@
 -- resulting in an executable game.
 module Main ( main ) where
 
-import qualified Game.LambdaHack.Kind as Kind
 import qualified Content.ActorKind
 import qualified Content.CaveKind
 import qualified Content.FactionKind
@@ -17,16 +17,26 @@
 import qualified Content.RuleKind
 import qualified Content.StrategyKind
 import qualified Content.TileKind
-import Game.LambdaHack.Turn
-import Game.LambdaHack.Action
-import Game.LambdaHack.BindingAction
+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
 
--- | Fire up the frontend with the engine fueled by content.
--- Which of the frontends is run depends on the flags supplied
--- when compiling the engine library.
+instance MonadAtomic ActionSer where
+  execAtomic = atomicSendSem
+
+instance MonadAtomic (ActionCli c) where
+  execAtomic (CmdAtomic cmd) = cmdAtomicSem cmd
+  execAtomic (SfxAtomic _) = return ()
+
 main :: IO ()
 main =
-  let cops = Kind.COps
+  let copsSlow = Kind.COps
         { coactor = Kind.createOps Content.ActorKind.cdefs
         , cocave  = Kind.createOps Content.CaveKind.cdefs
         , cofact  = Kind.createOps Content.FactionKind.cdefs
@@ -36,4 +46,4 @@
         , costrat = Kind.createOps Content.StrategyKind.cdefs
         , cotile  = Kind.createOps Content.TileKind.cdefs
         }
-  in startFrontend cops stdBinding handleTurn
+  in mainSer copsSlow executorSer $ exeFrontend executorCli executorCli
