diff --git a/CREDITS b/CREDITS
new file mode 100644
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,3 @@
+Space Privateers use LambdaHack engine. Many thanks to all people who have
+contributed on it. Space Privateers used original LambdaHack files as a 
+starting point for game definition.
diff --git a/GameDefinition/Client/UI/Content/KeyKind.hs b/GameDefinition/Client/UI/Content/KeyKind.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Client/UI/Content/KeyKind.hs
@@ -0,0 +1,171 @@
+-- | The default game key-command mapping to be used for UI. Can be overriden
+-- via macros in the config file.
+module Client.UI.Content.KeyKind ( standardKeys ) where
+
+import Control.Arrow (first)
+
+import qualified Game.LambdaHack.Client.Key as K
+import Game.LambdaHack.Client.UI.Content.KeyKind
+import Game.LambdaHack.Client.UI.HumanCmd
+import qualified Game.LambdaHack.Common.Effect as Effect
+import qualified Game.LambdaHack.Common.Feature as F
+import Game.LambdaHack.Common.Misc
+
+standardKeys :: KeyKind
+standardKeys = KeyKind
+  { rhumanCommands = map (first K.mkKM)
+      -- All commands are defined here, except some movement and leader picking
+      -- commands. All commands are shown on help screens except debug commands
+      -- and macros with empty descriptions.
+      -- The order below determines the order on the help screens.
+      -- Remember to put commands that show information (e.g., enter targeting
+      -- mode) first.
+
+      -- Main Menu, which apart of these includes a few extra commands
+      [ ("CTRL-x", ([CmdMenu], GameExit))
+      , ("CTRL-a", ([CmdMenu], GameRestart "campaign"))
+      , ("CTRL-d", ([CmdMenu], GameDifficultyCycle))
+
+      -- Movement and terrain alteration
+      , ("less", ([CmdMove, CmdMinimal], TriggerTile
+           [ TriggerFeature { verb = "ascend"
+                            , object = "a level"
+                            , feature = F.Cause (Effect.Ascend 1) }
+           , TriggerFeature { verb = "escape"
+                            , object = "dungeon"
+                            , feature = F.Cause (Effect.Escape 1) } ]))
+      , ("CTRL-less", ([CmdMove], TriggerTile
+           [ TriggerFeature { verb = "ascend"
+                            , object = "10 levels"
+                            , feature = F.Cause (Effect.Ascend 10) } ]))
+      , ("greater", ([CmdMove, CmdMinimal], TriggerTile
+           [ TriggerFeature { verb = "descend"
+                            , object = "a level"
+                            , feature = F.Cause (Effect.Ascend (-1)) }
+           , TriggerFeature { verb = "escape"
+                            , object = "dungeon"
+                            , feature = F.Cause (Effect.Escape (-1)) } ]))
+      , ("CTRL-greater", ([CmdMove], TriggerTile
+           [ TriggerFeature { verb = "descend"
+                            , object = "10 levels"
+                            , feature = F.Cause (Effect.Ascend (-10)) } ]))
+      , ("semicolon", ([CmdMove], StepToTarget))
+      , ("colon", ([CmdMove], Macro "go to target for 100 steps"
+                                    ["semicolon", "V"]))
+      , ("CTRL-colon", ([CmdMove], Macro "go to target for 10 steps"
+                                         ["semicolon", "CTRL-V"]))
+      , ("x", ([CmdMove], Macro "explore the closest unknown spot"
+                                [ "BackSpace"
+                                , "CTRL-question", "semicolon", "V" ]))
+      , ("X", ([CmdMove], Macro "autoexplore 100 times"
+                                [ "BackSpace"
+                                , "'", "CTRL-question", "semicolon", "'"
+                                , "V" ]))
+      , ("CTRL-X", ([CmdMove], Macro "autoexplore 10 times"
+                                      [ "BackSpace"
+                                      , "'", "CTRL-question", "semicolon", "'"
+                                      , "CTRL-V" ]))
+      , ("R", ([CmdMove], Macro "rest (wait 100 times)"
+                                ["KP_Begin", "V"]))
+      , ("CTRL-R", ([CmdMove], Macro "rest (wait 10 times)"
+                                     ["KP_Begin", "CTRL-V"]))
+      , ("c", ([CmdMove], AlterDir
+           [ AlterFeature { verb = "close"
+                          , object = "door"
+                          , feature = F.CloseTo "vertical closed door Lit" }
+           , AlterFeature { verb = "close"
+                          , object = "door"
+                          , feature = F.CloseTo "horizontal closed door Lit" }
+           , AlterFeature { verb = "close"
+                          , object = "door"
+                          , feature = F.CloseTo "vertical closed door Dark" }
+           , AlterFeature { verb = "close"
+                          , object = "door"
+                          , feature = F.CloseTo "horizontal closed door Dark" }
+           ]))
+      , ("period", ([CmdMove], Macro "" ["KP_Begin"]))
+      , ("i", ([CmdMove], Macro "" ["KP_Begin"]))
+
+      -- Item use
+      , ("E", ([CmdItem], DescribeItem CEqp))
+      , ("P", ([CmdItem], DescribeItem CInv))
+      , ("S", ([CmdItem], DescribeItem CSha))
+      , ("G", ([CmdItem], DescribeItem CGround))
+      , ("A", ([CmdItem], AllOwned))
+      , ("g", ([CmdItem, CmdMinimal],
+               MoveItem [CGround] CEqp "get" "an item" True))
+      , ("d", ([CmdItem], MoveItem [CEqp, CInv, CSha] CGround
+                                   "drop" "an item" False))
+      , ("e", ([CmdItem], MoveItem [CInv, CSha] CEqp
+                                   "equip" "an item" False))
+      , ("p", ([CmdItem], MoveItem [CEqp, CSha] CInv
+                                   "pack" "an item into inventory backpack"
+                                   False))
+      , ("s", ([CmdItem], MoveItem [CEqp, CInv] CSha
+                                   "stash" "and share an item" False))
+      , ("a", ([CmdItem, CmdMinimal], Apply
+           [ ApplyItem { verb = "activate"
+                       , object = "applicable item"
+                       , symbol = ' ' }
+           , ApplyItem { verb = "quaff"
+                       , object = "potion"
+                       , symbol = '!' }
+           , ApplyItem { verb = "read"
+                       , object = "scroll"
+                       , symbol = '?' }
+           ]))
+      , ("q", ([CmdItem], Apply [ApplyItem { verb = "quaff"
+                                           , object = "potion"
+                                           , symbol = '!' }]))
+      , ("r", ([CmdItem], Apply [ApplyItem { verb = "read"
+                                           , object = "scroll"
+                                           , symbol = '?' }]))
+      , ("f", ([CmdItem, CmdMinimal], Project
+           [ApplyItem { verb = "fling"
+                      , object = "projectable item"
+                      , symbol = ' ' }]))
+      , ("t", ([CmdItem], Project [ApplyItem { verb = "throw"
+                                             , object = "missile"
+                                             , symbol = '|' }]))
+      , ("z", ([CmdItem], Project [ApplyItem { verb = "zap"
+                                             , object = "wand"
+                                             , symbol = '/' }]))
+
+      -- Targeting
+      , ("KP_Multiply", ([CmdTgt, CmdMinimal], TgtEnemy))
+      , ("backslash", ([CmdTgt], Macro "" ["KP_Multiply"]))
+      , ("slash", ([CmdTgt], TgtFloor))
+      , ("plus", ([CmdTgt], EpsIncr True))
+      , ("minus", ([CmdTgt], EpsIncr False))
+      , ("BackSpace", ([CmdTgt], TgtClear))
+      , ("CTRL-question", ([CmdTgt], TgtUnknown))
+      , ("CTRL-I", ([CmdTgt], TgtItem))
+      , ("CTRL-braceleft", ([CmdTgt], TgtStair True))
+      , ("CTRL-braceright", ([CmdTgt], TgtStair False))
+
+      -- Automation
+      , ("equal", ([CmdAuto], SelectActor))
+      , ("underscore", ([CmdAuto], SelectNone))
+      , ("v", ([CmdAuto], Repeat 1))
+      , ("V", ([CmdAuto], Repeat 100))
+      , ("CTRL-v", ([CmdAuto], Repeat 1000))
+      , ("CTRL-V", ([CmdAuto], Repeat 10))
+      , ("apostrophe", ([CmdAuto], Record))
+      , ("CTRL-A", ([CmdAuto], Automate))
+
+      -- Assorted
+      , ("question", ([CmdMeta], Help))
+      , ("D", ([CmdMeta], History))
+      , ("T", ([CmdMeta], MarkSuspect))
+      , ("Z", ([CmdMeta], MarkVision))
+      , ("C", ([CmdMeta], MarkSmell))
+      , ("Tab", ([CmdMeta], MemberCycle))
+      , ("ISO_Left_Tab", ([CmdMeta], MemberBack))
+      , ("space", ([CmdMeta], Clear))
+      , ("Escape", ([CmdMeta, CmdMinimal], Cancel))
+      , ("Return", ([CmdMeta], Accept))
+
+      -- Debug and others not to display in help screens
+      , ("CTRL-s", ([CmdDebug], GameSave))
+      ]
+  }
diff --git a/GameDefinition/Content/CaveKind.hs b/GameDefinition/Content/CaveKind.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/CaveKind.hs
@@ -0,0 +1,69 @@
+-- | Cave layouts for Space Privateers.
+module Content.CaveKind ( cdefs ) where
+
+import Data.Ratio
+
+import Game.LambdaHack.Common.ContentDef
+import Game.LambdaHack.Common.Dice
+import Game.LambdaHack.Common.Misc
+import Game.LambdaHack.Content.CaveKind
+
+cdefs :: ContentDef CaveKind
+cdefs = ContentDef
+  { getSymbol = csymbol
+  , getName = cname
+  , getFreq = cfreq
+  , validate = validateCaveKind
+  , content =
+      [rogue, munitorium]
+  }
+rogue,        munitorium :: CaveKind
+
+rogue = CaveKind
+  { csymbol         = 'R'
+  , cname           = "General quarters"
+  , cfreq           = [("dng", 100), ("caveRogue", 1)]
+  , cxsize          = fst normalLevelBound + 1
+  , cysize          = snd normalLevelBound + 1
+  , cgrid           = DiceXY (3 * d 2) (d 2 + 2)
+  , cminPlaceSize   = DiceXY (2 * d 2 + 2) 4
+  , cmaxPlaceSize   = DiceXY 15 10
+  , cdarkChance     = d 54 + dl 20
+  , cnightChance    = 51
+  , cauxConnects    = 1%3
+  , cmaxVoid        = 1%6
+  , cminStairDist   = 30
+  , cdoorChance     = 1%2
+  , copenChance     = 1%10
+  , chidden         = 8
+  , cactorFreq      = [("merchant", 50), ("chaos", 45), ("spawn", 5)]
+  , citemNum        = 10 * d 2
+  , citemFreq       = [("useful", 70), ("treasure", 30)]
+  , cplaceFreq      = [("rogue", 100)]
+  , cpassable       = False
+  , cdefTile        = "fillerWall"
+  , cdarkCorTile    = "floorCorridorDark"
+  , clitCorTile     = "floorCorridorLit"
+  , cfillerTile     = "fillerWall"
+  , couterFenceTile = "basic outer fence"
+  , clegendDarkTile = "legendDark"
+  , clegendLitTile  = "legendLit"
+  }
+munitorium = rogue
+  { csymbol         = 'M'
+  , cname           = "Munitorium"
+  , cfreq           = [("dng", 15), ("munitorium", 1)]
+  , cgrid           = DiceXY 3 3
+  , cminPlaceSize   = DiceXY 15 4
+  , cmaxPlaceSize   = DiceXY 25 10
+  , cdarkChance     = dl 5
+  , cnightChance    = d 20
+  , cactorFreq      = [("merchant", 80), ("chaos", 5), ("spawn", 15)]
+  , citemNum        = 8 * d 2
+  , citemFreq       = [("weapon", 70), ("armour", 20), ("useful", 10)]
+  , cplaceFreq      = [("rogue", 100)]
+  , cpassable       = True
+  , cdefTile        = "arenaSet"
+  , cdarkCorTile    = "floorArenaDark"
+  , clitCorTile     = "floorArenaLit"
+}
diff --git a/GameDefinition/Content/FactionKind.hs b/GameDefinition/Content/FactionKind.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/FactionKind.hs
@@ -0,0 +1,73 @@
+-- | The type of kinds of game factions (heroes, enemies, NPCs, etc.)
+-- for Space Privateers.
+module Content.FactionKind ( cdefs ) where
+
+import qualified Data.EnumMap.Strict as EM
+
+import Game.LambdaHack.Common.Ability
+import Game.LambdaHack.Common.ContentDef
+import Game.LambdaHack.Content.FactionKind
+
+cdefs :: ContentDef FactionKind
+cdefs = ContentDef
+  { getSymbol = fsymbol
+  , getName = fname
+  , getFreq = ffreq
+  , validate = validateFactionKind
+  , content =
+      [hero, merchant, chaos, spawn, horror]
+  }
+hero,        merchant, chaos, spawn, horror :: FactionKind
+
+hero = FactionKind
+  { fsymbol       = '1'
+  , fname         = "hero"
+  , ffreq         = [("hero", 1)]
+  , fSkillsLeader = allSkills
+  , fSkillsOther  = meleeAdjacent
+  }
+
+merchant = FactionKind
+  { fsymbol       = '@'
+  , fname         = "merchant"
+  , ffreq         = [("merchant", 1)]
+  , fSkillsLeader = allSkills
+  , fSkillsOther  = allSkills
+  }
+
+chaos = FactionKind
+  { fsymbol       = 'c'
+  , fname         = "chaos"
+  , ffreq         = [("chaos", 1)]
+  , fSkillsLeader = allSkills
+  , fSkillsOther  = allSkills
+  }
+
+spawn = FactionKind
+  { fsymbol       = 's'
+  , fname         = "spawn"
+  , ffreq         = [("spawn", 1)]
+  , fSkillsLeader = animalSkills
+  , fSkillsOther  = animalSkills
+  } 
+
+horror = FactionKind
+  { fsymbol       = 'h'
+  , fname         = "horror"
+  , ffreq         = [("horror", 1)]
+  , fSkillsLeader = animalSkills
+  , fSkillsOther  = animalSkills
+  }
+
+
+meleeAdjacent, _meleeAndRanged, animalSkills, allSkills :: Skills
+
+meleeAdjacent = EM.fromList $ zip [AbWait, AbMelee] [1, 1..]
+
+-- Melee and reaction fire.
+_meleeAndRanged = EM.fromList $ zip [AbWait, AbMelee, AbProject] [1, 1..]
+
+animalSkills =
+  EM.fromList $ zip [AbMove, AbMelee, AbAlter, AbWait, AbTrigger] [1, 1..]
+
+allSkills = unitSkills
diff --git a/GameDefinition/Content/ItemKind.hs b/GameDefinition/Content/ItemKind.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/ItemKind.hs
@@ -0,0 +1,688 @@
+-- | Weapon and treasure definitions.
+module Content.ItemKind ( cdefs ) where
+
+import Data.List
+
+import Content.ItemKindActor
+import Content.ItemKindOrgan
+import Content.ItemKindShrapnel
+import Game.LambdaHack.Common.Color
+import Game.LambdaHack.Common.ContentDef
+import Game.LambdaHack.Common.Dice
+import Game.LambdaHack.Common.Effect
+import Game.LambdaHack.Common.Flavour
+import Game.LambdaHack.Common.Misc
+import Game.LambdaHack.Content.ItemKind
+
+cdefs :: ContentDef ItemKind
+cdefs = ContentDef
+  { getSymbol = isymbol
+  , getName = iname
+  , getFreq = ifreq
+  , validate = validateItemKind
+  , content = items ++ organs ++ shrapnels ++ actors
+  }
+
+items :: [ItemKind]
+items =
+  [decipulum, brassLantern, buckler, dart, dart200, gem1, gem2, gem3, gloveFencing, gloveGauntlet, gloveJousting, currency, gorget, harpoon, oculus, necklace1, necklace2, necklace3, necklace4, necklace5, necklace6, necklace7, net, oilLamp, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, ring1, ring2, ring3, ring4, ring5, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, shield, dagger, hammer, sword, halberd, wand1, wand2, armorLeather, armorMail, whetstone, grenade1, grenade2]
+
+decipulum,    brassLantern, buckler, dart, dart200, gem1, gem2, gem3, gloveFencing, gloveGauntlet, gloveJousting, currency, gorget, harpoon, oculus, necklace1, necklace2, necklace3, necklace4, necklace5, necklace6, necklace7, net, oilLamp, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, ring1, ring2, ring3, ring4, ring5, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, shield, dagger, hammer, sword, halberd, wand1, wand2, armorLeather, armorMail, whetstone, grenade1, grenade2 :: ItemKind
+
+gem, grenade, necklace, potion, ring, scroll, wand :: ItemKind  -- generic templates
+
+-- * Thrown weapons
+
+dart = ItemKind
+  { isymbol  = '|'
+  , iname    = "dart"
+  , ifreq    = [("useful", 100), ("any arrow", 100), ("weapon", 100)]
+  , iflavour = zipPlain [Cyan]
+  , icount   = 3 * d 3
+  , irarity  = [(1, 20)]
+  , iverbHit = "prick"
+  , iweight  = 50
+  , iaspects = [AddHurtRanged ((d 6 + dl 6) |*| 10)]
+  , ieffects = [Hurt (d 2)]
+  , ifeature = []
+  , idesc    = "Little, but sharp and sturdy."
+  , ikit     = []
+  }
+dart200 = ItemKind
+  { isymbol  = '|'
+  , iname    = "fine dart"
+  , ifreq    = [("useful", 100), ("any arrow", 50), ("weapon", 75)]
+  , iflavour = zipPlain [BrRed]
+  , icount   = 3 * d 3
+  , irarity  = [(4, 20)]
+  , iverbHit = "prick"
+  , iweight  = 50
+  , iaspects = [AddHurtRanged ((d 6 + dl 6) |*| 10)]
+  , ieffects = [Hurt (d 2)]
+  , ifeature = [toVelocity 200]
+  , idesc    = "Finely balanced for throws of great speed."
+  , ikit     = []
+  }
+
+-- * Exotic thrown weapons
+
+decipulum = ItemKind
+  { isymbol  = '|'
+  , iname    = "decipulum majoris"
+  , ifreq    = [("useful", 100), ("weapon", 50)]
+  , iflavour = zipPlain [BrYellow]
+  , icount   = dl 4
+  , irarity  = [(5, 5), (10, 5)]
+  , iverbHit = "entangle"
+  , iweight  = 500
+  , iaspects = []
+  , ieffects = [Hurt (d 2), Paralyze (5 + d 5), ActivateInv '!']
+  , ifeature = []
+  , idesc    = "A web of springs in form of spheres. When thrown, this weapon can trip unwary opponents."
+  , ikit     = []
+  }
+harpoon = ItemKind
+  { isymbol  = '|'
+  , iname    = "Force spear"
+  , ifreq    = [("useful", 100), ("starting weapon", 100), ("weapon", 50)]
+  , iflavour = zipPlain [Brown]
+  , icount   = dl 5
+  , irarity  = [(5, 5), (10, 20)]
+  , iverbHit = "hook"
+  , iweight  = 4000
+  , iaspects = [AddHurtRanged ((d 2 + 2 * dl 5) |*| 10)]
+  , ieffects = [Hurt (d 4), PullActor (ThrowMod 200 50)]
+  , ifeature = []
+  , idesc    = "The cruel, barbed head lodges in its victim so painfully that the weakest tug of the thin line sends the victim flying."
+  , ikit     = []
+  }
+net = ItemKind
+  { isymbol  = '|'
+  , iname    = "net"
+  , ifreq    = [("useful", 100), ("weapon", 60)]
+  , iflavour = zipPlain [White]
+  , icount   = dl 3
+  , irarity  = [(3, 5), (10, 4)]
+  , iverbHit = "entangle"
+  , iweight  = 1000
+  , iaspects = []
+  , ieffects = [ Hurt (d 2)
+               , Paralyze (5 + d 5)
+               , DropBestWeapon, DropEqp ']' False ]
+  , ifeature = []
+  , idesc    = "A web made of monofilaments. Will both cut and entagle the prey."
+  , ikit     = []
+  }
+
+-- * Lights
+
+oilLamp = ItemKind
+  { isymbol  = '('
+  , iname    = "oil lamp"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [BrYellow]
+  , icount   = 1
+  , irarity  = [(5, 4), (10, 4)]
+  , iverbHit = "burn"
+  , iweight  = 1000
+  , iaspects = [AddLight 3, AddSight (-1)]
+  , ieffects = [Burn 3, Paralyze 3, OnSmash (Explode "burning ignisium 3")]
+  , ifeature = [ toVelocity 70
+               , Fragile, EqpSlot EqpSlotAddLight "", Identified ]
+  , idesc    = "A clay lamp filled with ignisium feeding a tiny wick."
+  , ikit     = []
+  }
+brassLantern = ItemKind
+  { isymbol  = '('
+  , iname    = "brass lantern"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [BrWhite]
+  , icount   = 1
+  , irarity  = [(10, 3)]
+  , iverbHit = "burn"
+  , iweight  = 2400
+  , iaspects = [AddLight 4, AddSight (-1)]
+  , ieffects = [Burn 4, Paralyze 4, OnSmash (Explode "burning ignisium 4")]
+  , ifeature = [ toVelocity 70  -- hard to throw so that it opens and burns
+               , Fragile, EqpSlot EqpSlotAddLight "", Identified ]
+  , idesc    = "Very bright and very heavy brass lantern."
+  , ikit     = []
+  }
+
+-- * Treasure
+
+gem = ItemKind
+  { isymbol  = '*'
+  , iname    = "gem"
+  , ifreq    = [("treasure", 100)]
+  , iflavour = zipPlain $ delete BrYellow brightCol
+  , icount   = 1
+  , irarity  = []
+  , iverbHit = "tap"
+  , iweight  = 50
+  , iaspects = [AddLight 1, AddSpeed (-1)]
+  , ieffects = []
+  , ifeature = [ Durable
+               , Precious ]
+  , idesc    = "Useless, and still worth around 100 gold each. Would gems of thought and pearls of artful design be valued that much in our age of Science and Progress!"
+  , ikit     = []
+  }
+gem1 = gem
+  { irarity  = [(2, 0), (10, 10)]
+  }
+gem2 = gem
+  { irarity  = [(5, 0), (10, 10)]
+  }
+gem3 = gem
+  { irarity  = [(8, 0), (10, 10)]
+  }
+currency = ItemKind
+  { isymbol  = '$'
+  , iname    = "gold piece"
+  , ifreq    = [("treasure", 100), ("currency", 1)]
+  , iflavour = zipPlain [BrYellow]
+  , icount   = 10 + d 20 + dl 20
+  , irarity  = [(1, 0), (5, 20), (10, 10)]
+  , iverbHit = "tap"
+  , iweight  = 31
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [Durable, Identified, Precious]
+  , idesc    = "Reliably valuable in every civilized plane of existence."
+  , ikit     = []
+  }
+
+-- * Periodic jewelry
+
+gorget = ItemKind
+  { isymbol  = '"'
+  , iname    = "gorget"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipFancy [BrCyan]
+  , irarity  = [(4, 1), (10, 2)]
+  , icount   = 1
+  , iverbHit = "whip"
+  , iweight  = 30
+  , iaspects = [Periodic $ d 4 + dl 4, AddArmorMelee 1, AddArmorRanged 1]
+  , ieffects = [RefillCalm 1]
+  , ifeature = [ Precious, EqpSlot EqpSlotPeriodic "", Identified
+               , toVelocity 50 ]
+  , idesc    = "Highly ornamental, cold, large, steel medallion on a chain. Unlikely to offer much protection as an armor piece, but the old, worn engraving reassures you."
+  , ikit     = []
+  }
+necklace = ItemKind
+  { isymbol  = '"'
+  , iname    = "necklace"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipFancy stdCol ++ zipPlain brightCol
+  , irarity  = [(4, 2), (10, 5)]
+  , icount   = 1
+  , iverbHit = "whip"
+  , iweight  = 30
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [ Precious, EqpSlot EqpSlotPeriodic ""
+               , toVelocity 50 ]
+  , idesc    = "Menacing Greek symbols shimmer with increasing speeds along a chain of fine encrusted links. After a tense build-up, a prismatic arc shoots towards the ground and the iridescence subdues, becomes ordered and resembles a harmless ornament again, for a time."
+  , ikit     = []
+  }
+necklace1 = necklace
+  { iaspects = [Periodic $ d 2 + dl 2]
+  , ieffects = [RefillHP 1]
+  , idesc    = "A cord of dried herbs and healing berries."
+  }
+necklace2 = necklace
+  { irarity  = [(2, 0), (10, 1)]
+  , iaspects = [Periodic $ d 4 + dl 2]
+  , ieffects = [ Impress
+               , Summon [("summonable animal", 1)] $ 1 + dl 2, Explode "waste" ]
+  }
+necklace3 = necklace
+  { iaspects = [Periodic $ d 4 + dl 2]
+  , ieffects = [Paralyze $ 5 + d 5 + dl 5, RefillCalm 999]
+  }
+necklace4 = necklace
+  { iaspects = [Periodic $ 2 * d 10 + dl 10]
+  , ieffects = [Teleport $ 2 + d 3]
+  }
+necklace5 = necklace
+  { iaspects = [Periodic $ d 4 + dl 2]
+  , ieffects = [Teleport $ 10 + d 10]
+  }
+necklace6 = necklace
+  { iaspects = [Periodic $ 2 * d 5 + dl 5]
+  , ieffects = [PushActor (ThrowMod 100 50)]
+  }
+necklace7 = necklace
+  { irarity  = [(4, 0), (10, 2)]
+  , iaspects = [Periodic $ 2 * d 5 + dl 15]
+  , ieffects = [InsertMove 1, RefillHP (-1)]
+  , ifeature = ifeature necklace ++ [Durable]
+      -- evil players would throw before death, to destroy
+      -- TODO: teach AI to wear only for fight; prevent players from meleeing
+      -- allies with that (Durable)
+  }
+
+-- * Non-periodic jewelry
+
+oculus = ItemKind
+  { isymbol  = '='
+  , iname    = "oculus cursus"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [White]
+  , icount   = 1
+  , irarity  = [(6, 0), (10, 1)]
+  , iverbHit = "rap"
+  , iweight  = 50
+  , iaspects = [AddSight $ dl 3]
+  , ieffects = []
+  , ifeature = [Precious, Identified, Durable, EqpSlot EqpSlotAddSight ""]
+  , idesc    = "Arcane device to boost your sight."
+  , ikit     = []
+  }
+ring = ItemKind
+  { isymbol  = '='
+  , iname    = "ring"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain stdCol ++ zipFancy darkCol
+  , icount   = 1
+  , irarity  = [(6, 2), (10, 5)]
+  , iverbHit = "knock"
+  , iweight  = 15
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [Precious, Identified]
+  , idesc    = "It looks like an ordinary object, but it's in fact a generator of exceptional effects: adding to some of your natural abilities and subtracting from others. You'd profit enormously if you could find a way to multiply such generators..."
+  , ikit     = []
+  }
+ring1 = ring
+  { irarity  = [(2, 0), (10, 2)]
+  , iaspects = [AddSpeed 1, AddMaxHP $ dl 3 - 5 - d 3]
+  , ifeature = ifeature ring ++ [Durable, EqpSlot EqpSlotAddSpeed ""]
+  }
+ring2 = ring
+  { iaspects = [AddMaxHP $ 3 + dl 5, AddMaxCalm $ dl 6 - 15 - d 6]
+  , ifeature = ifeature ring ++ [EqpSlot EqpSlotAddMaxHP ""]
+  }
+ring3 = ring
+  { iaspects = [AddMaxCalm $ 10 + dl 10]
+  , ifeature = ifeature ring ++ [EqpSlot EqpSlotAddMaxCalm ""]
+  , idesc    = "Cold, solid to the touch, perfectly round, engraved with solemn, strangely comforting, worn out words."
+  }
+ring4 = ring  -- TODO: move to level-ups and to timed effects
+  { irarity  = [(3, 8), (10, 12)]
+  , iaspects = [AddHurtMelee $ 3 * d 4 + dl 15, AddMaxHP $ dl 3 - 4 - d 2]
+  , ifeature = ifeature ring ++ [Durable, EqpSlot EqpSlotAddHurtMelee ""]
+  }
+ring5 = ring  -- by the time it's found, probably no space in eqp
+  { irarity  = [(5, 0), (10, 1)]
+  , iaspects = [AddLight $ d 2]
+  , ifeature = ifeature ring ++ [EqpSlot EqpSlotAddLight ""]
+  , idesc    = "A sturdy ring with a large, shining stone."
+  }
+
+-- * Exploding consumables, often intended to be thrown
+
+potion = ItemKind
+  { isymbol  = '!'
+  , iname    = "vial"
+  , ifreq    = [("useful", 100), ("any vial", 100)]
+  , iflavour = zipPlain stdCol ++ zipFancy brightCol
+  , icount   = 1
+  , irarity  = [(1, 10), (10, 8)]
+  , iverbHit = "splash"
+  , iweight  = 200
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [ toVelocity 50
+               , Applicable, Fragile ]
+  , idesc    = "A flask of bubbly, slightly oily liquid of a suspect color."
+  , ikit     = []
+  }
+potion1 = potion
+  { ieffects = [ NoEffect "of rose water", Impress
+               , OnSmash (ApplyPerfume), OnSmash (Explode "fragrance") ]
+  }
+potion2 = potion
+  { ifreq    = [("useful", 1)]
+  , irarity  = [(10, 1)]
+  , ieffects = [ NoEffect "of musky concoction", Impress, DropBestWeapon
+               , OnSmash (Explode "pheromone")]
+  }
+potion3 = potion
+  { ieffects = [RefillHP 5, OnSmash (Explode "healing mist")]
+  }
+potion4 = potion
+  { irarity  = [(1, 5)]
+  , ieffects = [RefillHP (-5), OnSmash (Explode "wounding mist")]
+  }
+potion5 = potion
+  { ieffects = [ Explode "explosion blast 10", Impress
+               , PushActor (ThrowMod 200 75)
+               , OnSmash (Explode "explosion blast 10") ]
+  }
+potion6 = potion
+  { irarity  = [(10, 2)]
+  , ieffects = [ NoEffect "of distortion"
+               , OnSmash (Explode "distortion")]
+  }
+potion7 = potion
+  { ieffects = [ NoEffect "of bait cocktail", Impress
+               , OnSmash (Summon [("summonable animal", 1)] $ 1 + dl 2)
+               , OnSmash (Explode "waste") ]
+  }
+potion8 = potion
+  { ieffects = [ OneOf [Impress, DropBestWeapon, RefillHP 5, Burn 3]
+               , OnSmash (OneOf [ Explode "healing mist"
+                                , Explode "wounding mist"
+                                , Explode "fragrance"
+                                , Explode "explosion blast 10" ]) ]
+  }
+potion9 = potion
+  { irarity  = [(4, 1), (10, 2)]
+  , ieffects = [ OneOf [ Dominate, DropBestWeapon, RefillHP 15, Burn 9
+                       , InsertMove 2]
+               , OnSmash (OneOf [ Explode "healing mist"
+                                , Explode "healing mist"
+                                , Explode "pheromone"
+                                , Explode "distortion"
+                                , Explode "explosion blast 20" ]) ]
+  }
+potion10 = potion
+  { ifreq    = [("useful", 100), ("potion of glue", 1)]
+  , irarity  = [(1, 1)]
+  , icount   = 1 + d 2
+  , ieffects = [ NoEffect "of glue", Paralyze (5 + d 5)
+               , OnSmash (Explode "glue")]
+  }
+grenade = ItemKind
+  { isymbol  = '*'
+  , iname    = "grenade"
+  , ifreq    = [("useful", 100), ("weapon", 25)]
+  , iflavour = zipPlain stdCol ++ zipFancy brightCol
+  , icount   = 1
+  , irarity  = [(1, 1), (10, 3)]
+  , iverbHit = "explode"
+  , iweight  = 200
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [ toVelocity 200
+               , Fragile 
+               , Identified ]
+  , idesc    = "Small metal canister, with safety latch and finely engraved surface."
+  , ikit     = []
+  }
+grenade1 = grenade
+  { irarity  = [(1, 1), (10, 3)]
+  , ieffects = [ NoEffect "of explosionis"
+               , OnSmash (Explode "explosion blast 10") ]
+  }
+grenade2 = grenade
+  { irarity  = [(1, 1), (10, 3)]
+  , ieffects = [ NoEffect "of ignisium"
+               , OnSmash (Explode "burning ignisium 2") ]
+  }
+-- * Non-exploding consumables, not specifically designed for throwing
+
+scroll = ItemKind
+  { isymbol  = '?'
+  , iname    = "scroll"
+  , ifreq    = [("useful", 100), ("any scroll", 100)]
+  , iflavour = zipFancy stdCol ++ zipPlain darkCol
+  , icount   = 1
+  , irarity  = [(1, 10), (10, 7)]
+  , iverbHit = "thump"
+  , iweight  = 50
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [ toVelocity 25
+               , Applicable ]
+  , idesc    = "Scraps of haphazardly scribbled mysteries from beyond. Is this equation an alchemical recipe? Is this diagram an extradimensional map? Is this formula a secret call sign?"
+  , ikit     = []
+  }
+scroll1 = scroll
+  { irarity  = [(10, 2)]
+  , ieffects = [CallFriend 1]
+  }
+scroll2 = scroll
+  { irarity  = [(1, 5), (10, 3)]
+  , ieffects = [NoEffect "of fireworks", Explode "firecracker 7"]
+  }
+scroll3 = scroll
+  { irarity  = [(1, 4), (10, 2)]
+  , ieffects = [Ascend (-1)]
+  }
+scroll4 = scroll
+  { ieffects = [ OneOf [ Teleport $ 2 + d 5, RefillCalm 10, RefillCalm (-10)
+                       , InsertMove 4, Paralyze 10, Identify CGround ] ]
+  }
+scroll5 = scroll
+  { irarity  = [(1, 4), (10, 6)]
+  , ieffects = [ OneOf [ Summon standardSummon $ d 2
+                       , CallFriend 1, Ascend (-1), Ascend 1
+                       , RefillCalm 30, RefillCalm (-30), CreateItem $ d 2
+                       , PolyItem CGround ] ]
+               -- TODO: ask player: Escape 1
+  }
+scroll6 = scroll
+  { ieffects = [Teleport $ 2 + d 5]
+  }
+scroll7 = scroll
+  { irarity  = [(10, 2)]
+  , ieffects = [InsertMove $ d 2 + dl 2]
+  }
+scroll8 = scroll
+  { irarity  = [(3, 6), (10, 3)]
+  , ieffects = [Identify CGround]  -- TODO: ask player: AskPlayer cstore eff?
+  }
+scroll9 = scroll
+  { irarity  = [(3, 3), (10, 9)]
+  , ieffects = [PolyItem CGround]
+  }
+
+standardSummon :: Freqs
+standardSummon = [("monster", 30), ("summonable animal", 70)]
+
+-- * Armor
+
+armorLeather = ItemKind
+  { isymbol  = '['
+  , iname    = "leather armor"
+  , ifreq    = [("useful", 100), ("armour", 100)]
+  , iflavour = zipPlain [Brown]
+  , icount   = 1
+  , irarity  = [(4, 9)]
+  , iverbHit = "thud"
+  , iweight  = 7000
+  , iaspects = [ AddHurtMelee (-3)
+               , AddArmorMelee $ (1 + dl 3) |*| 5
+               , AddArmorRanged $ (1 + dl 3) |*| 5 ]
+  , ieffects = []
+  , ifeature = [ toVelocity 30
+               , Durable, EqpSlot EqpSlotAddArmorMelee "", Identified ]
+  , idesc    = "A stiff jacket formed from leather boiled in bee wax. Smells much better than the rest of your garment."
+  , ikit     = []
+  }
+armorMail = armorLeather
+  { iname    = "mail armor"
+  , iflavour = zipPlain [Cyan]
+  , irarity  = [(7, 9)]
+  , iweight  = 12000
+  , iaspects = [ AddHurtMelee (-3)
+               , AddArmorMelee $ (2 + dl 3) |*| 5
+               , AddArmorRanged $ (2 + dl 3) |*| 5 ]
+  , idesc    = "A long shirt woven from iron rings. Discourages foes from attacking your torso, making it harder for them to land a blow."
+  }
+gloveFencing = ItemKind
+  { isymbol  = '['
+  , iname    = "leather gauntlet"
+  , ifreq    = [("useful", 100), ("armour", 100)]
+  , iflavour = zipPlain [BrYellow]
+  , icount   = 1
+  , irarity  = [(4, 6), (10, 12)]
+  , iverbHit = "flap"
+  , iweight  = 100
+  , iaspects = [ AddHurtMelee $ 2 * (d 3 + 2 * dl 5)
+               , AddArmorRanged $ d 2 + dl 2 ]
+  , ieffects = []
+  , ifeature = [ toVelocity 30
+               , Durable, EqpSlot EqpSlotAddArmorRanged "", Identified ]
+  , idesc    = "A fencing glove from rough leather ensuring a good grip. Also quite effective in deflecting or even catching slow projectiles."
+  , ikit     = []
+  }
+gloveGauntlet = gloveFencing
+  { iname    = "steel gauntlet"
+  , irarity  = [(6, 12)]
+  , iflavour = zipPlain [BrCyan]
+  , iweight  = 300
+  , iaspects = [ AddArmorMelee $ 2 * (d 2 + dl 2)
+               , AddArmorRanged $ 2 * (d 2 + dl 2) ]
+  , idesc    = "Long leather gauntlet covered in overlapping steel plates."
+  }
+gloveJousting = gloveFencing
+  { iname    = "jousting gauntlet"
+  , irarity  = [(6, 6)]
+  , iflavour = zipFancy [BrRed]
+  , iweight  = 500
+  , iaspects = [ AddHurtMelee $ - 10 - d 5 + dl 5
+               , AddArmorMelee $ 2 * (d 2 + dl 3)
+               , AddArmorRanged $ 2 * (d 2 + dl 3) ]
+  , idesc    = "Rigid, steel, jousting handgear. If only you had a lance. And a horse."
+  }
+
+buckler = ItemKind
+  { isymbol  = '['
+  , iname    = "buckler"
+  , ifreq    = [("useful", 100), ("armour", 100)]
+  , iflavour = zipPlain [Blue]
+  , icount   = 1
+  , irarity  = [(4, 7)]
+  , iverbHit = "bash"
+  , iweight  = 2000
+  , iaspects = [AddArmorMelee 40, AddHurtMelee (-30)]
+  , ieffects = []
+  , ifeature = [ toVelocity 30
+               , Durable, EqpSlot EqpSlotAddArmorMelee "", Identified ]
+  , idesc    = "Heavy and unwieldy. Absorbs a percentage of melee damage, both dealt and sustained. Too small to intercept projectiles with."
+  , ikit     = []
+  }
+shield = buckler
+  { iname    = "shield"
+  , irarity  = [(7, 7)]
+  , iflavour = zipPlain [Green]
+  , iweight  = 3000
+  , iaspects = [AddArmorMelee 80, AddHurtMelee (-70)]
+  , ifeature = [ toVelocity 20
+               , Durable, EqpSlot EqpSlotAddArmorMelee "", Identified ]
+  , idesc    = "Large and unwieldy. Absorbs a percentage of melee damage, both dealt and sustained. Too heavy to intercept projectiles with."
+  }
+
+-- * Weapons
+
+dagger = ItemKind
+  { isymbol  = ')'
+  , iname    = "dagger"
+  , ifreq    = [("useful", 100), ("starting weapon", 100), ("weapon", 100)]
+  , iflavour = zipPlain [BrCyan]
+  , icount   = 1
+  , irarity  = [(1, 20), (10, 4)]
+  , iverbHit = "stab"
+  , iweight  = 1000
+  , iaspects = [AddHurtMelee $ 2 * (d 3 + 2 * dl 5), AddArmorMelee $ d 4 + dl 4]
+  , ieffects = [Hurt (d 4)]
+  , ifeature = [ toVelocity 40
+               , Durable, EqpSlot EqpSlotWeapon "", Identified ]
+  , idesc    = "A short dagger for thrusting and parrying blows. Does not penetrate deeply, but is hard to block. Especially useful in conjunction with a larger weapon."
+  , ikit     = []
+  }
+hammer = ItemKind
+  { isymbol  = ')'
+  , iname    = "storm hammer"
+  , ifreq    = [("useful", 100), ("weapon", 60), ("starting weapon", 100)]
+  , iflavour = zipPlain [BrMagenta]
+  , icount   = 1
+  , irarity  = [(4, 12), (10, 2)]
+  , iverbHit = "club"
+  , iweight  = 1500
+  , iaspects = [AddHurtMelee $ d 3 + 2 * dl 5]
+  , ieffects = [Hurt (d 6), PushActor (ThrowMod 200 50)]
+  , ifeature = [ toVelocity 20
+               , Durable, EqpSlot EqpSlotWeapon "", Identified ]
+  , idesc    = "Mighty hammer that releases blast of electricity when it hits target."
+  , ikit     = []
+  }
+sword = ItemKind
+  { isymbol  = ')'
+  , iname    = "sword"
+  , ifreq    = [("useful", 100), ("starting weapon", 100), ("weapon", 75)]
+  , iflavour = zipPlain [BrBlue]
+  , icount   = 1
+  , irarity  = [(3, 1), (6, 20), (10, 10)]
+  , iverbHit = "slash"
+  , iweight  = 2000
+  , iaspects = []
+  , ieffects = [Hurt (d 8)]
+  , ifeature = [ toVelocity 20
+               , Durable, EqpSlot EqpSlotWeapon "", Identified ]
+  , idesc    = "Common vibroblade, favoured by those who like to get close and personal."
+  , ikit     = []
+  }
+halberd = ItemKind
+  { isymbol  = ')'
+  , iname    = "halberd"
+  , ifreq    = [("useful", 100), ("weapon", 50)]
+  , iflavour = zipPlain [BrYellow]
+  , icount   = 1
+  , irarity  = [(7, 1), (10, 10)]
+  , iverbHit = "impale"
+  , iweight  = 3000
+  , iaspects = [AddArmorMelee $ 2 * (d 4 + dl 4)]
+  , ieffects = [Hurt (d 12)]
+  , ifeature = [ toVelocity 20
+               , Durable, EqpSlot EqpSlotWeapon "", Identified ]
+  , idesc    = "Versatile, with great reach and leverage. Foes are held at a distance."
+  , ikit     = []
+  }
+
+-- * Wands
+
+wand = ItemKind
+  { isymbol  = '/'
+  , iname    = "wand"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipFancy brightCol
+  , icount   = 1
+  , irarity  = []  -- TODO: add charges, etc.
+  , iverbHit = "club"
+  , iweight  = 300
+  , iaspects = [AddLight 1, AddSpeed (-1)]
+  , ieffects = []
+  , ifeature = [ toVelocity 125
+               , Applicable, Durable ]
+  , idesc    = "Buzzing with dazzling light that shines even through appendages that handle it."
+  , ikit     = []
+  }
+wand1 = wand
+  { ieffects = []
+  }
+wand2 = wand
+  { ieffects = []
+  }
+
+-- * Assorted tools
+
+whetstone = ItemKind
+  { isymbol  = '~'
+  , iname    = "whetstone"
+  , ifreq    = [("useful", 100)]
+  , iflavour = zipPlain [Blue]
+  , icount   = 1
+  , irarity  = [(5, 5)]
+  , iverbHit = "smack"
+  , iweight  = 400
+  , iaspects = [AddHurtMelee $ 2 * (d 3 + 2 * dl 5)]
+  , ieffects = []
+  , ifeature = [EqpSlot EqpSlotAddHurtMelee "", Identified]
+  , idesc    = "A portable sharpening stone that lets you fix your weapons between or even during fights, without the need to set up camp, fish out tools and assemble a proper sharpening workshop."
+  , ikit     = []
+  }
diff --git a/GameDefinition/Content/ItemKindActor.hs b/GameDefinition/Content/ItemKindActor.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/ItemKindActor.hs
@@ -0,0 +1,165 @@
+-- | Actor (or rather actor body trunk) definitions.
+module Content.ItemKindActor ( actors ) where
+
+import qualified Data.EnumMap.Strict as EM
+
+import Game.LambdaHack.Common.Ability
+import Game.LambdaHack.Common.Color
+import Game.LambdaHack.Common.Effect
+import Game.LambdaHack.Common.Flavour
+import Game.LambdaHack.Common.Misc
+import Game.LambdaHack.Content.ItemKind
+
+actors :: [ItemKind]
+actors = privateers ++ merchants ++ forcesOfChaos ++ spawns ++ animals
+
+privateers, merchants, forcesOfChaos, spawns, animals :: [ItemKind]
+
+privateers    = [warrior, scout]
+merchants     = [merchant, merchantSgt]
+forcesOfChaos = [chaosWarrior, chaosLord]
+spawns        = [warpBeast]
+animals       = [ventLizard]
+
+warrior, scout :: ItemKind
+merchant, merchantSgt :: ItemKind
+chaosWarrior, chaosLord :: ItemKind
+warpBeast :: ItemKind
+ventLizard :: ItemKind
+
+-- * Heroes (hero)
+
+warrior = ItemKind
+  { isymbol  = '@'
+  , iname    = "warrior"
+  , ifreq    = [("hero", 100)]
+  , iflavour = zipPlain [BrBlack]
+  , icount   = 1
+  , irarity  = [(1, 5)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 50, AddMaxCalm 60, AddSpeed 20
+               , AddSight 3 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "PBI, ready to take the next city vessel."
+  , ikit     = [("fist", COrgan), ("foot", COrgan), ("eye 4", COrgan),
+                ("starting weapon", CEqp)]
+  }
+
+scout = warrior
+  { iname    = "scout"
+  , iaspects = [ AddMaxHP 40, AddMaxCalm 60, AddSpeed 25
+               , AddSight 4 ]
+  , idesc    = "Fast and keen scouts are essential any fighting force."
+  , ikit     = [("fist", COrgan), ("foot", COrgan), ("eye 5", COrgan),
+                ("any arrow", CInv), ("any vial", CInv), ("any vial", CInv)]
+  }
+
+-- * Merchant Mariners (merchant)
+
+merchant = ItemKind
+  { isymbol  = '@'
+  , iname    = "merchant mariner"
+  , ifreq    = [("merchant", 100)]
+  , iflavour = zipPlain [Blue]
+  , icount   = 1
+  , irarity  = [(1, 10)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 30, AddMaxCalm 40, AddSpeed 20
+               , AddSight 3 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Merchant mariners are operating city vessels on their endless journey to stars."
+  , ikit     = [("fist", COrgan), ("foot", COrgan), ("eye 4", COrgan)]
+  }
+
+merchantSgt = merchant
+  { iname    = "merchant mariner sgt"
+  , ifreq    = [("merchant", 100)]
+  , iflavour = zipPlain [BrBlue]
+  , icount   = 1
+  , irarity  = [(1, 1), (10, 3)]
+  , iverbHit = "thud"
+  , iweight  = 80000
+  , iaspects = [ AddMaxHP 40, AddMaxCalm 60, AddSpeed 20
+               , AddSight 3 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Squad leader for merchant mariners"
+  , ikit     = [("fist", COrgan), ("foot", COrgan), ("eye 4", COrgan),
+                ("starting weapon", CEqp)]
+  }
+
+-- * Forces of Chaos (chaos)
+
+chaosWarrior = ItemKind
+  { isymbol  = 'c'
+  , iname    = "chaos warrior"
+  , ifreq    = [("chaos", 100)]
+  , iflavour = zipPlain [BrBlack]
+  , icount   = 1
+  , irarity  = [(1, 5)]
+  , iverbHit = "thud"
+  , iweight  = 100000
+  , iaspects = [ AddMaxHP 40, AddMaxCalm 70, AddSpeed 20
+               , AddSight 3 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Warrior of Chaos, ready to wreck havoc."
+  , ikit     = [("fist", COrgan), ("foot", COrgan), ("eye 4", COrgan),
+                ("starting weapon", CEqp)]
+  }
+
+chaosLord    = chaosWarrior
+  { isymbol  = 'c'
+  , iname    = "chaos lord"
+  , ifreq    = [("chaos", 100)]
+  , irarity  = [(5, 1), (10, 3)]
+  , iflavour = zipPlain [BrRed]
+  , iaspects = [ AddMaxHP 60, AddMaxCalm 70, AddSpeed 20
+               , AddSight 3 ]
+  , idesc    = "Lord of Chaos, leader of warriors"
+  }
+
+-- * Horrors (spawn)
+
+warpBeast    = ItemKind
+  { isymbol  = 'w'
+  , iname    = "warp beast"
+  , ifreq    = [("spawn", 100)]
+  , iflavour = zipPlain [BrRed]
+  , icount   = 1
+  , irarity  = [(1, 5)]
+  , iverbHit = "thud"
+  , iweight  = 40000
+  , iaspects = [ AddMaxHP 20, AddMaxCalm 30, AddSpeed 20
+               , AddSight 1 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = "Shambling beast of warp has phased into this corporeal realm."
+  , ikit     = [("small claw", COrgan), ("foot", COrgan), ("eye 2", COrgan),
+                ("nostril", COrgan)]
+  }
+
+-- * Animals
+
+ventLizard = ItemKind
+  { isymbol  = ':'
+  , iname    = "vent lizard"
+  , ifreq    = [("summonable animal", 100)]
+  , iflavour = zipPlain [Green]
+  , icount   = 1
+  , irarity  = [(1, 5)]
+  , iverbHit = "thud"
+  , iweight  = 25000
+  , iaspects = [ AddMaxHP 15, AddMaxCalm 30, AddSpeed 18
+               , AddSkills $ EM.singleton AbAlter (-1)
+               , AddSight 3 ]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  , ikit     = [ ("claw", COrgan), ("snout", COrgan), ("armored skin", COrgan)
+               , ("nostril", COrgan) ]
+  }
diff --git a/GameDefinition/Content/ItemKindOrgan.hs b/GameDefinition/Content/ItemKindOrgan.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/ItemKindOrgan.hs
@@ -0,0 +1,259 @@
+-- | Organ definitions.
+module Content.ItemKindOrgan ( organs ) where
+
+import Game.LambdaHack.Common.Color
+import Game.LambdaHack.Common.Dice
+import Game.LambdaHack.Common.Effect
+import Game.LambdaHack.Common.Flavour
+import Game.LambdaHack.Common.Msg
+import Game.LambdaHack.Content.ItemKind
+
+organs :: [ItemKind]
+organs =
+  [fist, foot, tentacle, lash, noseTip, lip, claw, smallClaw, snout, sting, venomTooth, venomFang, largeTail, jaw, largeJaw, tooth, pupil, armoredSkin, speedGland2, speedGland4, speedGland6, speedGland8, speedGland10, eye2, eye3, eye4, eye5, nostril, thorn, vent, fissure]
+
+fist,    foot, tentacle, lash, noseTip, lip, claw, smallClaw, snout, sting, venomTooth, venomFang, largeTail, jaw, largeJaw, tooth, pupil, armoredSkin, speedGland2, speedGland4, speedGland6, speedGland8, speedGland10, eye2, eye3, eye4, eye5, nostril, thorn, vent, fissure :: ItemKind
+
+-- * Parameterized organs
+
+speedGland :: Int -> ItemKind
+speedGland n = fist
+  { iname    = "speed gland"
+  , ifreq    = [("speed gland" <+> tshow n, 100)]
+  , icount   = 1
+  , iverbHit = "spit at"
+  , iaspects = [AddSpeed $ intToDice n, Periodic $ intToDice n]
+  , ieffects = [RefillHP 1]
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
+speedGland2 = speedGland 2
+speedGland4 = speedGland 4
+speedGland6 = speedGland 6
+speedGland8 = speedGland 8
+speedGland10 = speedGland 10
+eye :: Int -> ItemKind
+eye n = fist
+  { iname    = "eye"
+  , ifreq    = [("eye" <+> tshow n, 100)]
+  , icount   = 2
+  , iverbHit = "glare at"
+  , iaspects = [AddSight (intToDice n)]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
+eye2 = eye 2
+eye3 = eye 3
+eye4 = eye 4
+eye5 = eye 5
+
+-- * Human weapon organs
+
+fist = ItemKind
+  { isymbol  = '%'
+  , iname    = "fist"
+  , ifreq    = [("fist", 100)]
+  , iflavour = zipPlain [BrRed]
+  , icount   = 2
+  , irarity  = [(1, 1)]
+  , iverbHit = "punch"
+  , iweight  = 2000
+  , iaspects = []
+  , ieffects = [Hurt (d 2)]
+  , ifeature = [Durable, EqpSlot EqpSlotWeapon "", Identified]
+  , idesc    = ""
+  , ikit     = []
+  }
+foot = fist
+  { iname    = "foot"
+  , ifreq    = [("foot", 50)]
+  , icount   = 2
+  , iverbHit = "kick"
+  , ieffects = [Hurt (d 2)]
+  , idesc    = ""
+  }
+
+-- * Universal weapon organs
+
+claw = fist
+  { iname    = "claw"
+  , ifreq    = [("claw", 50)]
+  , icount   = 2
+  , iverbHit = "slash"
+  , ieffects = [Hurt (d 4)]
+  , idesc    = ""
+  }
+smallClaw = fist
+  { iname    = "small claw"
+  , ifreq    = [("small claw", 50)]
+  , icount   = 2
+  , iverbHit = "slash"
+  , ieffects = [Hurt (d 2)]
+  , idesc    = ""
+  }
+snout = fist
+  { iname    = "snout"
+  , ifreq    = [("snout", 10)]
+  , iverbHit = "bite"
+  , ieffects = [Hurt (d 2)]
+  , idesc    = ""
+  }
+jaw = fist
+  { iname    = "jaw"
+  , ifreq    = [("jaw", 20)]
+  , icount   = 1
+  , iverbHit = "rip"
+  , ieffects = [Hurt (d 4)]
+  , idesc    = ""
+  }
+largeJaw = fist
+  { iname    = "large jaw"
+  , ifreq    = [("large jaw", 100)]
+  , icount   = 1
+  , iverbHit = "crush"
+  , ieffects = [Hurt (d 6)]
+  , idesc    = ""
+  }
+tooth = fist
+  { iname    = "tooth"
+  , ifreq    = [("tooth", 20)]
+  , icount   = 3
+  , iverbHit = "nail"
+  , ieffects = [Hurt (d 4)]
+  , idesc    = ""
+  }
+
+-- * Monster weapon organs
+
+tentacle = fist
+  { iname    = "tentacle"
+  , ifreq    = [("tentacle", 50)]
+  , icount   = 4
+  , iverbHit = "slap"
+  , ieffects = [Hurt (d 4)]
+  , idesc    = ""
+  }
+lash = fist
+  { iname    = "lash"
+  , ifreq    = [("lash", 100)]
+  , icount   = 1
+  , iverbHit = "lash"
+  , ieffects = [Hurt (d 6)]
+  , idesc    = ""
+  }
+noseTip = fist
+  { iname    = "tip"
+  , ifreq    = [("nose tip", 50)]
+  , icount   = 1
+  , iverbHit = "poke"
+  , ieffects = [Hurt (d 4)]
+  , idesc    = ""
+  }
+lip = fist
+  { iname    = "lip"
+  , ifreq    = [("lip", 10)]
+  , icount   = 2
+  , iverbHit = "lap"
+  , ieffects = [Hurt (d 2)]
+  , idesc    = ""
+  }
+
+-- * Special weapon organs
+
+thorn = fist
+  { iname    = "thorn"
+  , ifreq    = [("thorn", 100)]
+  , icount   = 7
+  , iverbHit = "impale"
+  , ieffects = [Hurt (d 2)]
+  , idesc    = ""
+  }
+fissure = fist
+  { iname    = "fissure"
+  , ifreq    = [("fissure", 100)]
+  , icount   = 2
+  , iverbHit = "hiss at"
+  , ieffects = [Burn 1]
+  , idesc    = ""
+  }
+sting = fist
+  { iname    = "sting"
+  , ifreq    = [("sting", 100)]
+  , icount   = 1
+  , iverbHit = "sting"
+  , ieffects = [Burn 1, Paralyze 2]
+  , idesc    = ""
+  }
+venomTooth = fist
+  { iname    = "venom tooth"
+  , ifreq    = [("venom tooth", 100)]
+  , icount   = 2
+  , iverbHit = "bite"
+  , ieffects = [Hurt (d 2), Paralyze 3]
+  , idesc    = ""
+  }
+venomFang = fist
+  { iname    = "venom fang"
+  , ifreq    = [("venom fang", 100)]
+  , icount   = 2
+  , iverbHit = "bite"
+  , ieffects = [Hurt (d 4)]
+  , idesc    = ""
+  }
+largeTail = fist
+  { iname    = "large tail"
+  , ifreq    = [("large tail", 50)]
+  , icount   = 1
+  , iverbHit = "knock"
+  , ieffects = [Hurt (d 8), PushActor (ThrowMod 400 25)]
+  , idesc    = ""
+  }
+pupil = fist
+  { iname    = "pupil"
+  , ifreq    = [("pupil", 100)]
+  , icount   = 1
+  , iverbHit = "gaze at"
+  , iaspects = [AddSight 7]
+  , ieffects = [Hurt (d 2), Paralyze 1]
+  , idesc    = ""
+  }
+
+-- * Armor organs
+
+armoredSkin = fist
+  { iname    = "armored skin"
+  , ifreq    = [("armored skin", 100)]
+  , icount   = 1
+  , iverbHit = "bash"
+  , iaspects = [AddArmorMelee 33, AddArmorRanged 33]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
+
+-- * Sense organs
+
+nostril = fist
+  { iname    = "nostril"
+  , ifreq    = [("nostril", 100)]
+  , icount   = 2
+  , iverbHit = "snuff"
+  , iaspects = [AddSmell 1]
+  , ieffects = []
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
+
+-- * Assorted
+
+vent = fist
+  { iname    = "vent"
+  , ifreq    = [("vent", 100)]
+  , icount   = 1
+  , iverbHit = "menace"
+  , iaspects = [Periodic $ 1 + d 2]
+  , ieffects = [Explode "boiling water"]
+  , ifeature = [Durable, Identified]
+  , idesc    = ""
+  }
diff --git a/GameDefinition/Content/ItemKindShrapnel.hs b/GameDefinition/Content/ItemKindShrapnel.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/ItemKindShrapnel.hs
@@ -0,0 +1,239 @@
+-- | Shrapnel definitions.
+module Content.ItemKindShrapnel ( shrapnels ) where
+
+import Game.LambdaHack.Common.Color
+import Game.LambdaHack.Common.Dice
+import Game.LambdaHack.Common.Effect
+import Game.LambdaHack.Common.Flavour
+import Game.LambdaHack.Common.Msg
+import Game.LambdaHack.Content.ItemKind
+
+shrapnels :: [ItemKind]
+shrapnels =
+  [fragrance, pheromone, firecracker2, firecracker3, firecracker4, firecracker5, firecracker6, firecracker7, mistHealing, mistWounding, distortion, waste, burningIgnisium2, burningIgnisium3, burningIgnisium4, explosionBlast10, explosionBlast20, glassPiece, smoke, boilingWater, glue]
+
+fragrance,    pheromone, firecracker2, firecracker3, firecracker4, firecracker5, firecracker6, firecracker7, mistHealing, mistWounding, distortion, waste, burningIgnisium2, burningIgnisium3, burningIgnisium4, explosionBlast10, explosionBlast20, glassPiece, smoke, boilingWater, glue :: ItemKind
+
+-- * Parameterized shrapnel
+
+burningIgnisium :: Int -> ItemKind
+burningIgnisium n = ItemKind
+  { isymbol  = '*'
+  , iname    = "burning ignisium"
+  , ifreq    = [("burning ignisium" <+> tshow n, 1)]
+  , iflavour = zipFancy [BrYellow]
+  , icount   = intToDice (n * 5)
+  , irarity  = [(1, 1)]
+  , iverbHit = "burn"
+  , iweight  = 1
+  , iaspects = [AddLight 3]
+  , ieffects = [ Burn (n `div` 2)
+               , Paralyze (intToDice $ n `div` 2) ]
+  , ifeature = [ toVelocity (min 100 $ n * 7)
+               , Fragile, Identified ]
+  , idesc    = "Flaming ignisium, burning brightly."
+  , ikit     = []
+  }
+burningIgnisium2 = burningIgnisium 2
+burningIgnisium3 = burningIgnisium 3
+burningIgnisium4 = burningIgnisium 4
+explosionBlast :: Int -> ItemKind
+explosionBlast n = ItemKind
+  { isymbol  = '*'
+  , iname    = "explosion blast"
+  , ifreq    = [("explosion blast" <+> tshow n, 1)]
+  , iflavour = zipPlain [BrRed]
+  , icount   = 12  -- strong, but few, so not always hits target
+  , irarity  = [(1, 1)]
+  , iverbHit = "tear apart"
+  , iweight  = 1
+  , iaspects = [AddLight $ intToDice n]
+  , ieffects = [RefillHP (- n `div` 2), DropBestWeapon]
+  , ifeature = [Fragile, toLinger 10, Identified]
+  , idesc    = ""
+  , ikit     = []
+  }
+explosionBlast10 = explosionBlast 10
+explosionBlast20 = explosionBlast 20
+firecracker :: Int -> ItemKind
+firecracker n = ItemKind
+  { isymbol  = '*'
+  , iname    = "firecracker"
+  , ifreq    = [("firecracker" <+> tshow n, 1)]
+  , iflavour = zipPlain [stdCol !! (n `mod` length stdCol)]
+  , icount   = intToDice $ 2 * n
+  , irarity  = [(1, 1)]
+  , iverbHit = "crack"
+  , iweight  = 1
+  , iaspects = [AddLight $ intToDice $ n `div` 2]
+  , ieffects = [Burn 1, Explode $ "firecracker" <+> tshow (n - 1)]
+  , ifeature = [ ToThrow $ ThrowMod (n * 10) 20
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+firecracker7 = firecracker 7
+firecracker6 = firecracker 6
+firecracker5 = firecracker 5
+firecracker4 = firecracker 4
+firecracker3 = firecracker 3
+firecracker2 = firecracker 2
+
+-- * Assorted
+
+fragrance = ItemKind
+  { isymbol  = '\''
+  , iname    = "fragrance"
+  , ifreq    = [("fragrance", 1)]
+  , iflavour = zipFancy [Magenta]
+  , icount   = 15
+  , irarity  = [(1, 1)]
+  , iverbHit = "engulf"
+  , iweight  = 1
+  , iaspects = []
+  , ieffects = [Impress]
+  , ifeature = [ toVelocity 13  -- the slowest that travels at least 2 steps
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+pheromone = ItemKind
+  { isymbol  = '\''
+  , iname    = "musky whiff"
+  , ifreq    = [("pheromone", 1)]
+  , iflavour = zipFancy [BrMagenta]
+  , icount   = 8
+  , irarity  = [(1, 1)]
+  , iverbHit = "tempt"
+  , iweight  = 1
+  , iaspects = []
+  , ieffects = [Dominate]
+  , ifeature = [ toVelocity 13  -- the slowest that travels at least 2 steps
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+mistHealing = ItemKind
+  { isymbol  = '\''
+  , iname    = "mist"
+  , ifreq    = [("healing mist", 1)]
+  , iflavour = zipFancy [White]
+  , icount   = 11
+  , irarity  = [(1, 1)]
+  , iverbHit = "revitalize"
+  , iweight  = 1
+  , iaspects = [AddLight 1]
+  , ieffects = [RefillHP 2]
+  , ifeature = [ toVelocity 7  -- the slowest that gets anywhere (1 step only)
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+mistWounding = ItemKind
+  { isymbol  = '\''
+  , iname    = "mist"
+  , ifreq    = [("wounding mist", 1)]
+  , iflavour = zipFancy [White]
+  , icount   = 13
+  , irarity  = [(1, 1)]
+  , iverbHit = "devitalize"
+  , iweight  = 1
+  , iaspects = []
+  , ieffects = [RefillHP (-2)]
+  , ifeature = [ toVelocity 7  -- the slowest that gets anywhere (1 step only)
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+distortion = ItemKind
+  { isymbol  = 'v'
+  , iname    = "vortex"
+  , ifreq    = [("distortion", 1)]
+  , iflavour = zipFancy [White]
+  , icount   = 4
+  , irarity  = [(1, 1)]
+  , iverbHit = "engulf"
+  , iweight  = 1
+  , iaspects = []
+  , ieffects = [Teleport $ 15 + d 10]
+  , ifeature = [ toVelocity 7  -- the slowest that gets anywhere (1 step only)
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+waste = ItemKind
+  { isymbol  = '*'
+  , iname    = "waste"
+  , ifreq    = [("waste", 1)]
+  , iflavour = zipPlain [Brown]
+  , icount   = 10
+  , irarity  = [(1, 1)]
+  , iverbHit = "splosh"
+  , iweight  = 50
+  , iaspects = []
+  , ieffects = [RefillHP (-1)]
+  , ifeature = [ ToThrow $ ThrowMod 28 50
+               , Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+glassPiece = ItemKind  -- when blowing up windows
+  { isymbol  = '*'
+  , iname    = "glass piece"
+  , ifreq    = [("glass piece", 1)]
+  , iflavour = zipPlain [BrBlue]
+  , icount   = 17
+  , irarity  = [(1, 1)]
+  , iverbHit = "cut"
+  , iweight  = 10
+  , iaspects = []
+  , ieffects = [Hurt (1 * d 1)]
+  , ifeature = [toLinger 20, Fragile, Identified]
+  , idesc    = ""
+  , ikit     = []
+  }
+smoke = ItemKind  -- when stuff burns out
+  { isymbol  = '\''
+  , iname    = "smoke"
+  , ifreq    = [("smoke", 1), ("firecracker 1", 1)]
+  , iflavour = zipPlain [BrBlack]
+  , icount   = 19
+  , irarity  = [(1, 1)]
+  , iverbHit = "choke"
+  , iweight  = 1
+  , iaspects = []
+  , ieffects = []
+  , ifeature = [ toVelocity 21, Fragile, Identified ]
+  , idesc    = ""
+  , ikit     = []
+  }
+boilingWater = ItemKind
+  { isymbol  = '*'
+  , iname    = "boiling water"
+  , ifreq    = [("boiling water", 1)]
+  , iflavour = zipPlain [BrWhite]
+  , icount   = 9
+  , irarity  = [(1, 1)]
+  , iverbHit = "boil"
+  , iweight  = 5
+  , iaspects = []
+  , ieffects = [Burn 1]
+  , ifeature = [toVelocity 50, Fragile, Identified]
+  , idesc    = ""
+  , ikit     = []
+  }
+glue = ItemKind
+  { isymbol  = '*'
+  , iname    = "hoof glue"
+  , ifreq    = [("glue", 1)]
+  , iflavour = zipPlain [BrYellow]
+  , icount   = 14
+  , irarity  = [(1, 1)]
+  , iverbHit = "glue"
+  , iweight  = 20
+  , iaspects = []
+  , ieffects = [Paralyze (3 + d 3)]
+  , ifeature = [toVelocity 40, Fragile, Identified]
+  , idesc    = ""
+  , ikit     = []
+  }
diff --git a/GameDefinition/Content/ModeKind.hs b/GameDefinition/Content/ModeKind.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/ModeKind.hs
@@ -0,0 +1,113 @@
+-- | The type of kinds of game modes for Space Privateers.
+module Content.ModeKind ( cdefs ) where
+
+import qualified Data.IntMap.Strict as IM
+
+import Game.LambdaHack.Common.ContentDef
+import Game.LambdaHack.Content.ModeKind
+
+cdefs :: ContentDef ModeKind
+cdefs = ContentDef
+  { getSymbol = msymbol
+  , getName = mname
+  , getFreq = mfreq
+  , validate = validateModeKind
+  , content =
+      [campaign]
+  }
+campaign :: ModeKind
+
+campaign = ModeKind
+  { msymbol  = 'a'
+  , mname    = "campaign"
+  , mfreq    = [("campaign", 1)]
+  , mplayers = playersCampaign
+  , mcaves   = cavesCampaign
+  , mdesc    = "Get ready to assault the city vessel and plunder some loot!"
+  }
+
+playersCampaign :: Players
+
+playersCampaign = Players
+  { playersList = [ playerHero
+                  , playerMerchant
+                  , playerChaos
+                  , playerHorror
+                  , playerSpawn ]
+  , playersEnemy = [ ("Space Privateers", "Merchant Mariners")
+                   , ("Space Privateers", "Forces of Chaos") 
+                   , ("Space Privateers", "Spawns of Warp")
+                   , ("Merchant Mariners", "Forces of Chaos") 
+                   , ("Merchant Mariners", "Spawns of Warp") ]
+  , playersAlly = [("Forces of Chaos", "Spawns of Warp")] }
+
+playerHero, playerMerchant, playerChaos, playerSpawn, playerHorror :: Player
+
+playerHero = Player
+  { playerName = "Space Privateers"
+  , playerFaction = "hero"
+  , playerIsSpawn = False
+  , playerIsHero = True
+  , playerEntry = -1
+  , playerInitial = 3
+  , playerLeader = True
+  , playerAI = False
+  , playerUI = True
+  }
+
+playerMerchant = Player
+  { playerName = "Merchant Mariners"
+  , playerFaction = "merchant"
+  , playerIsSpawn = True
+  , playerIsHero = False
+  , playerEntry = -1
+  , playerInitial = 3
+  , playerLeader = False
+  , playerAI = True
+  , playerUI = False
+  }
+
+playerChaos = Player
+  { playerName = "Forces of Chaos"
+  , playerFaction = "chaos"
+  , playerIsSpawn = True
+  , playerIsHero = False
+  , playerEntry = -1
+  , playerInitial = 5
+  , playerLeader = False
+  , playerAI = True
+  , playerUI = False
+  }
+
+playerHorror = Player
+  { playerName = "Horrors of Warp"
+  , playerFaction = "horror"
+  , playerIsSpawn = True
+  , playerIsHero = False
+  , playerEntry = -1
+  , playerInitial = 0
+  , playerLeader = False
+  , playerAI = True
+  , playerUI = False
+  }
+
+playerSpawn = Player
+  { playerName = "Spawns of Warp"
+  , playerFaction = "spawn"
+  , playerIsSpawn = True
+  , playerIsHero = False
+  , playerEntry = -1
+  , playerInitial = 0
+  , playerLeader = False
+  , playerAI = True
+  , playerUI = False
+  }
+
+cavesCampaign :: Caves
+
+cavesCampaign = IM.fromList $ [ (-1, ("caveRogue", Just True))
+                              , (-2, ("caveRogue", Nothing))
+                              , (-3, ("caveRogue", Nothing)) ]
+                              ++ zip [-4, -5..(-9)] (repeat ("dng", Nothing))
+                              ++ [(-10, ("caveRogue", Nothing))]
+
diff --git a/GameDefinition/Content/PlaceKind.hs b/GameDefinition/Content/PlaceKind.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/PlaceKind.hs
@@ -0,0 +1,185 @@
+-- | Rooms, halls and passages for Space Privateers.
+module Content.PlaceKind ( cdefs ) where
+
+import Game.LambdaHack.Common.ContentDef
+import Game.LambdaHack.Content.PlaceKind
+
+cdefs :: ContentDef PlaceKind
+cdefs = ContentDef
+  { getSymbol = psymbol
+  , getName = pname
+  , getFreq = pfreq
+  , validate = validatePlaceKind
+  , content =
+      [rect, ruin, collapsed, collapsed2, collapsed3, collapsed4, pillar, pillarC, pillar3, colonnade, colonnade2, colonnade3, lampPost, lampPost2, lampPost3, lampPost4, treeShade, treeShade2, treeShade3]
+  }
+rect,        ruin, collapsed, collapsed2, collapsed3, collapsed4, pillar, pillarC, pillar3, colonnade, colonnade2, colonnade3, lampPost, lampPost2, lampPost3, lampPost4, treeShade, treeShade2, treeShade3 :: PlaceKind
+
+rect = PlaceKind  -- Valid for any nonempty area, hence low frequency.
+  { psymbol  = 'r'
+  , pname    = "room"
+  , pfreq    = [("rogue", 100), ("ambush", 8)]
+  , prarity  = [(1, 1)]
+  , pcover   = CStretch
+  , pfence   = FNone
+  , ptopLeft = [ "--"
+               , "|."
+               ]
+  , poverride = []
+  }
+ruin = PlaceKind
+  { psymbol  = 'R'
+  , pname    = "ruin"
+  , pfreq    = [("ambush", 17), ("battle", 100)]
+  , prarity  = [(1, 1)]
+  , pcover   = CStretch
+  , pfence   = FNone
+  , ptopLeft = [ "--"
+               , "|X"
+               ]
+  , poverride = []
+  }
+collapsed = PlaceKind
+  { psymbol  = 'c'
+  , pname    = "collapsed cavern"
+  , pfreq    = [("noise", 1)]
+  , prarity  = [(1, 1)]
+  , pcover   = CStretch
+  , pfence   = FNone
+  , ptopLeft = [ "O"
+               ]
+  , poverride = []
+  }
+collapsed2 = collapsed
+  { pfreq    = [("noise", 100), ("battle", 50)]
+  , ptopLeft = [ "XXO"
+               , "XOO"
+               ]
+  }
+collapsed3 = collapsed
+  { pfreq    = [("noise", 200), ("battle", 50)]
+  , ptopLeft = [ "XXXO"
+               , "XOOO"
+               ]
+  }
+collapsed4 = collapsed
+  { pfreq    = [("noise", 400), ("battle", 200)]
+  , ptopLeft = [ "XXXO"
+               , "XXXO"
+               , "XOOO"
+               ]
+  }
+pillar = PlaceKind
+  { psymbol  = 'p'
+  , pname    = "pillar room"
+  , pfreq    = [("rogue", 1000)]  -- larger rooms require support pillars
+  , prarity  = [(1, 1)]
+  , pcover   = CStretch
+  , pfence   = FNone
+  , ptopLeft = [ "-----"
+               , "|...."
+               , "|.O.."
+               , "|...."
+               , "|...."
+               ]
+  , poverride = []
+  }
+pillarC = pillar
+  { ptopLeft = [ "-----"
+               , "|O..."
+               , "|...."
+               , "|...."
+               , "|...."
+               ]
+  }
+pillar3 = pillar
+  { ptopLeft = [ "-----"
+               , "|&.O."
+               , "|...."
+               , "|O..."
+               , "|...."
+               ]
+  }
+colonnade = PlaceKind
+  { psymbol  = 'c'
+  , pname    = "colonnade"
+  , pfreq    = [("rogue", 60)]
+  , prarity  = [(1, 1)]
+  , pcover   = CAlternate
+  , pfence   = FFloor
+  , ptopLeft = [ "O."
+               , ".O"
+               ]
+  , poverride = []
+  }
+colonnade2 = colonnade
+  { ptopLeft = [ "O."
+               , ".."
+               ]
+  }
+colonnade3 = colonnade
+  { pfreq    = [("rogue", 6)]
+  , ptopLeft = [ ".."
+               , ".O"
+               ]
+  }
+lampPost = PlaceKind
+  { psymbol  = 'l'
+  , pname    = "lamp post"
+  , pfreq    = [("ambush", 30), ("battle", 10)]
+  , prarity  = [(1, 1)]
+  , pcover   = CVerbatim
+  , pfence   = FNone
+  , ptopLeft = [ "X.X"
+               , ".O."
+               , "X.X"
+               ]
+  , poverride = [('O', "lampPostOver_O")]
+  }
+lampPost2 = lampPost
+  { ptopLeft = [ "..."
+               , ".O."
+               , "..."
+               ]
+  }
+lampPost3 = lampPost
+  { ptopLeft = [ "XX.XX"
+               , "X...X"
+               , "..O.."
+               , "X...X"
+               , "XX.XX"
+               ]
+  }
+lampPost4 = lampPost
+  { ptopLeft = [ "X...X"
+               , "....."
+               , "..O.."
+               , "....."
+               , "X...X"
+               ]
+  }
+treeShade = PlaceKind
+  { psymbol  = 't'
+  , pname    = "tree shade"
+  , pfreq    = [("skirmish", 100)]
+  , prarity  = [(1, 1)]
+  , pcover   = CVerbatim
+  , pfence   = FNone
+  , ptopLeft = [ "sss"
+               , "XOs"
+               , "XXs"
+               ]
+  , poverride = [('O', "treeShadeOver_O"), ('s', "treeShadeOver_s")]
+  }
+treeShade2 = treeShade
+  { ptopLeft = [ "sss"
+               , "XOs"
+               , "Xss"
+               ]
+  }
+treeShade3 = treeShade
+  { ptopLeft = [ "sss"
+               , "sOs"
+               , "XXs"
+               ]
+  }
diff --git a/GameDefinition/Content/RuleKind.hs b/GameDefinition/Content/RuleKind.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/RuleKind.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE TemplateHaskell #-}
+-- | Game rules and assorted game setup data for Space Privateer.
+module Content.RuleKind ( cdefs ) where
+
+import Language.Haskell.TH.Syntax
+import System.FilePath
+
+-- Cabal
+import qualified Paths_SpacePrivateers as Self (getDataFileName, version)
+
+import Game.LambdaHack.Common.ContentDef
+import Game.LambdaHack.Common.Vector
+import Game.LambdaHack.Content.RuleKind
+
+cdefs :: ContentDef RuleKind
+cdefs = ContentDef
+  { getSymbol = rsymbol
+  , getName = rname
+  , getFreq = rfreq
+  , validate = validateRuleKind
+  , content =
+      [standard]
+  }
+
+standard :: RuleKind
+standard = RuleKind
+  { rsymbol        = 's'
+  , rname          = "standard Space Privateers ruleset"
+  , rfreq          = [("standard", 100)]
+  , raccessible    = Nothing
+  , raccessibleDoor =
+      Just $ \spos tpos -> not $ isDiagonal $ spos `vectorToFrom` tpos
+  , rtitle         = "Space Privateers"
+  , rpathsDataFile = Self.getDataFileName
+  , rpathsVersion  = Self.version
+  , rcfgUIName = "config.ui"
+  , rcfgUIDefault = $(do
+      let path = "GameDefinition" </> "config.ui" <.> "default"
+      qAddDependentFile path
+      x <- qRunIO (readFile path)
+      lift x)
+  , rmainMenuArt = $(do
+      let path = "GameDefinition/MainMenu.ascii"
+      qAddDependentFile path
+      x <- qRunIO (readFile path)
+      lift x)
+  , rfirstDeathEnds = False
+  , rfovMode = Digital
+  , rsaveBkpClips = 500
+  , rleadLevelClips = 100
+  , rscoresFile = "scores"
+  , rsavePrefix = "save"
+  , rsharedStash = True
+  }
diff --git a/GameDefinition/Content/TileKind.hs b/GameDefinition/Content/TileKind.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Content/TileKind.hs
@@ -0,0 +1,307 @@
+-- | Terrain tiles for Space Privateers
+module Content.TileKind ( cdefs ) where
+
+import Control.Arrow (first)
+import Data.Maybe
+import Data.Text (Text)
+import qualified Data.Text as T
+
+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.Common.Msg
+import Game.LambdaHack.Content.TileKind
+
+cdefs :: ContentDef TileKind
+cdefs = ContentDef
+  { getSymbol = tsymbol
+  , getName = tname
+  , getFreq = tfreq
+  , validate = validateTileKind
+  , content =
+      [wall, hardRock, pillar, pillarCache, lampPost, burningBush, bush, tree, wallV, wallSuspectV, doorClosedV, doorOpenV, wallH, wallSuspectH, doorClosedH, doorOpenH, stairsUpLit, stairsLit, stairsDownLit, escapeUpLit, escapeDownLit, unknown, floorCorridorLit, floorArenaLit, floorArenaShade, floorActorLit, floorItemLit, floorActorItemLit, floorRedLit, floorBlueLit, floorGreenLit, floorBrownLit]
+      ++ map makeDark [wallV, wallSuspectV, doorClosedV, doorOpenV, wallH, wallSuspectH, doorClosedH, doorOpenH, stairsLit, escapeUpLit, escapeDownLit, floorCorridorLit]
+      ++ map makeDarkColor [stairsUpLit, stairsDownLit, floorArenaLit, floorActorLit, floorItemLit, floorActorItemLit]
+  }
+wall,        hardRock, pillar, pillarCache, lampPost, burningBush, bush, tree, wallV, wallSuspectV, doorClosedV, doorOpenV, wallH, wallSuspectH, doorClosedH, doorOpenH, stairsUpLit, stairsLit, stairsDownLit, escapeUpLit, escapeDownLit, unknown, floorCorridorLit, floorArenaLit, floorArenaShade, floorActorLit, floorItemLit, floorActorItemLit, floorRedLit, floorBlueLit, floorGreenLit, floorBrownLit :: TileKind
+
+wall = TileKind
+  { tsymbol  = ' '
+  , tname    = "bedrock"
+  , tfreq    = [("fillerWall", 1), ("legendLit", 100), ("legendDark", 100)]
+  , tcolor   = defBG
+  , tcolor2  = defBG
+  , tfeature = [Dark]
+      -- Bedrock being dark is bad for AI (forces it to backtrack to explore
+      -- bedrock at corridor turns) and induces human micromanagement
+      -- if there can be corridors joined diagonally (humans have to check
+      -- with the cursor if the dark space is bedrock or unexplored).
+      -- Lit bedrock would be even worse for humans, because it's harder
+      -- to guess which tiles are unknown and which can be explored bedrock.
+      -- The setup of Allure is ideal, with lit bedrock that is easily
+      -- distinguished from an unknown tile. However, LH follows the NetHack,
+      -- not the Angband, visual tradition, so we can't improve the situation,
+      -- unless we turn to subtle shades of black or non-ASCII glyphs,
+      -- but that is yet different aesthetics and it's inconsistent
+      -- with console frontends.
+  }
+hardRock = TileKind
+  { tsymbol  = ' '
+  , tname    = "impenetrable bedrock"
+  , tfreq    = [("basic outer fence", 1)]
+  , tcolor   = BrWhite
+  , tcolor2  = BrWhite
+  , tfeature = [Dark, Impenetrable]
+  }
+pillar = TileKind
+  { tsymbol  = 'O'
+  , tname    = "rock"
+  , tfreq    = [ ("cachable", 70)
+               , ("legendLit", 100), ("legendDark", 100)
+               , ("noiseSet", 100), ("skirmishSet", 5)
+               , ("battleSet", 250) ]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
+  , tfeature = []
+  }
+pillarCache = TileKind
+  { tsymbol  = '&'
+  , tname    = "cache"
+  , tfreq    = [ ("cachable", 30)
+               , ("legendLit", 100), ("legendDark", 100) ]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
+  , tfeature = [Cause $ Effect.CreateItem 1, ChangeTo "cachable"]
+  }
+lampPost = TileKind
+  { tsymbol  = 'O'
+  , tname    = "lamp post"
+  , tfreq    = [("lampPostOver_O", 90)]
+  , tcolor   = BrYellow
+  , tcolor2  = Brown
+  , tfeature = []
+  }
+burningBush = TileKind
+  { tsymbol  = 'O'
+  , tname    = "burning bush"
+  , tfreq    = [("lampPostOver_O", 10), ("ambushSet", 3), ("battleSet", 2)]
+  , tcolor   = BrRed
+  , tcolor2  = Red
+  , tfeature = []
+  }
+bush = TileKind
+  { tsymbol  = 'O'
+  , tname    = "bush"
+  , tfreq    = [("ambushSet", 100) ]
+  , tcolor   = Green
+  , tcolor2  = BrBlack
+  , tfeature = [Dark]
+  }
+tree = TileKind
+  { tsymbol  = 'O'
+  , tname    = "tree"
+  , tfreq    = [("skirmishSet", 14), ("battleSet", 20), ("treeShadeOver_O", 1)]
+  , tcolor   = BrGreen
+  , tcolor2  = Green
+  , tfeature = []
+  }
+wallV = TileKind
+  { tsymbol  = '|'
+  , tname    = "granite wall"
+  , tfreq    = [("legendLit", 100)]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
+  , tfeature = [HideAs "suspect vertical wall Lit"]
+  }
+wallSuspectV = TileKind
+  { tsymbol  = '|'
+  , tname    = "moldy wall"
+  , tfreq    = [("suspect vertical wall Lit", 1)]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
+  , tfeature = [Suspect, RevealAs "vertical closed door Lit"]
+  }
+doorClosedV = TileKind
+  { tsymbol  = '+'
+  , tname    = "closed door"
+  , tfreq    = [("vertical closed door Lit", 1)]
+  , tcolor   = Brown
+  , tcolor2  = BrBlack
+  , tfeature = [ OpenTo "vertical open door Lit"
+               , HideAs "suspect vertical wall Lit"
+               ]
+  }
+doorOpenV = TileKind
+  { tsymbol  = '-'
+  , tname    = "open door"
+  , tfreq    = [("vertical open door Lit", 1)]
+  , tcolor   = Brown
+  , tcolor2  = BrBlack
+  , tfeature = [ Walkable, Clear, NoItem, NoActor
+               , CloseTo "vertical closed door Lit"
+               ]
+  }
+wallH = TileKind
+  { tsymbol  = '-'
+  , tname    = "granite wall"
+  , tfreq    = [("legendLit", 100)]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
+  , tfeature = [HideAs "suspect horizontal wall Lit"]
+  }
+wallSuspectH = TileKind
+  { tsymbol  = '-'
+  , tname    = "scratched wall"
+  , tfreq    = [("suspect horizontal wall Lit", 1)]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
+  , tfeature = [Suspect, RevealAs "horizontal closed door Lit"]
+  }
+doorClosedH = TileKind
+  { tsymbol  = '+'
+  , tname    = "closed door"
+  , tfreq    = [("horizontal closed door Lit", 1)]
+  , tcolor   = Brown
+  , tcolor2  = BrBlack
+  , tfeature = [ OpenTo "horizontal open door Lit"
+               , HideAs "suspect horizontal wall Lit"
+               ]
+  }
+doorOpenH = TileKind
+  { tsymbol  = '|'
+  , tname    = "open door"
+  , tfreq    = [("horizontal open door Lit", 1)]
+  , tcolor   = Brown
+  , tcolor2  = BrBlack
+  , tfeature = [ Walkable, Clear, NoItem, NoActor
+               , CloseTo "horizontal closed door Lit"
+               ]
+  }
+stairsUpLit = TileKind
+  { tsymbol  = '<'
+  , tname    = "staircase up"
+  , tfreq    = [("legendLit", 100)]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
+  , tfeature = [Walkable, Clear, NoItem, NoActor, Cause $ Effect.Ascend 1]
+  }
+stairsLit = TileKind
+  { tsymbol  = '>'
+  , tname    = "staircase"
+  , tfreq    = [("legendLit", 100)]
+  , tcolor   = BrCyan
+  , tcolor2  = Cyan  -- TODO
+  , tfeature = [ Walkable, Clear, NoItem, NoActor
+               , Cause $ Effect.Ascend 1
+               , Cause $ Effect.Ascend (-1) ]
+  }
+stairsDownLit = TileKind
+  { tsymbol  = '>'
+  , tname    = "staircase down"
+  , tfreq    = [("legendLit", 100)]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
+  , tfeature = [Walkable, Clear, NoItem, NoActor, Cause $ Effect.Ascend (-1)]
+  }
+escapeUpLit = TileKind
+  { tsymbol  = '<'
+  , tname    = "exit trapdoor up"
+  , tfreq    = [("legendLit", 100)]
+  , tcolor   = BrYellow
+  , tcolor2  = BrYellow
+  , tfeature = [Walkable, Clear, NoItem, NoActor, Cause $ Effect.Escape 1]
+  }
+escapeDownLit = TileKind
+  { tsymbol  = '>'
+  , tname    = "exit trapdoor down"
+  , tfreq    = [("legendLit", 100)]
+  , tcolor   = BrYellow
+  , tcolor2  = BrYellow
+  , tfeature = [Walkable, Clear, NoItem, NoActor, Cause $ Effect.Escape (-1)]
+  }
+unknown = TileKind
+  { tsymbol  = ' '
+  , tname    = "unknown space"
+  , tfreq    = [("unknown space", 1)]
+  , tcolor   = defFG
+  , tcolor2  = defFG
+  , tfeature = [Dark]
+  }
+floorCorridorLit = TileKind
+  { tsymbol  = '#'
+  , tname    = "corridor"
+  , tfreq    = [("floorCorridorLit", 1)]
+  , tcolor   = BrWhite
+  , tcolor2  = defFG
+  , tfeature = [Walkable, Clear]
+  }
+floorArenaLit = floorCorridorLit
+  { tsymbol  = '.'
+  , tname    = "stone floor"
+  , tfreq    = [ ("floorArenaLit", 1)
+               , ("arenaSet", 1), ("emptySet", 1), ("noiseSet", 50)
+               , ("battleSet", 1000), ("skirmishSet", 100)
+               , ("ambushSet", 1000) ]
+  }
+floorActorLit = floorArenaLit
+  { tfreq    = []
+  , tfeature = OftenActor : tfeature floorArenaLit
+  }
+floorItemLit = floorArenaLit
+  { tfreq    = []
+  , tfeature = OftenItem : tfeature floorArenaLit
+  }
+floorActorItemLit = floorItemLit
+  { tfreq    = [("legendLit", 100)]  -- no OftenItem in legendDark
+  , tfeature = OftenActor : tfeature floorItemLit
+  }
+floorArenaShade = floorActorLit
+  { tname    = "stone floor"  -- TODO: "shaded ground"
+  , tfreq    = [("treeShadeOver_s", 1)]
+  , tcolor2  = BrBlack
+  , tfeature = Dark : tfeature floorActorLit  -- no OftenItem
+  }
+floorRedLit = floorArenaLit
+  { tname    = "brick pavement"
+  , tfreq    = [("trailLit", 30)]
+  , tcolor   = BrRed
+  , tcolor2  = Red
+  , tfeature = Trail : tfeature floorArenaLit
+  }
+floorBlueLit = floorRedLit
+  { tname    = "granite cobblestones"
+  , tfreq    = [("trailLit", 100)]
+  , tcolor   = BrBlue
+  , tcolor2  = Blue
+  }
+floorGreenLit = floorRedLit
+  { tname    = "mossy stone path"
+  , tfreq    = [("trailLit", 100)]
+  , tcolor   = BrGreen
+  , tcolor2  = Green
+  }
+floorBrownLit = floorRedLit
+  { tname    = "rotting mahogany deck"
+  , tfreq    = [("trailLit", 10)]
+  , tcolor   = BrMagenta
+  , tcolor2  = Magenta
+  }
+
+makeDark :: TileKind -> TileKind
+makeDark k = let darkText :: Text -> Text
+                 darkText t = maybe t (<> "Dark") $ T.stripSuffix "Lit" t
+                 darkFrequency = map (first darkText) $ tfreq k
+                 darkFeat (OpenTo t) = Just $ OpenTo $ darkText t
+                 darkFeat (CloseTo t) = Just $ CloseTo $ darkText t
+                 darkFeat (ChangeTo t) = Just $ ChangeTo $ darkText t
+                 darkFeat (HideAs t) = Just $ HideAs $ darkText t
+                 darkFeat (RevealAs t) = Just $ RevealAs $ darkText t
+                 darkFeat OftenItem = Nothing
+                 darkFeat feat = Just $ feat
+             in k { tfreq    = darkFrequency
+                  , tfeature = Dark : mapMaybe darkFeat (tfeature k)
+                  }
+
+makeDarkColor :: TileKind -> TileKind
+makeDarkColor k = (makeDark k) {tcolor2 = BrBlack}
diff --git a/GameDefinition/Main.hs b/GameDefinition/Main.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/Main.hs
@@ -0,0 +1,13 @@
+-- | The main source code file of Space Privateers the game.
+module Main ( main ) where
+
+import System.Environment (getArgs)
+
+import TieKnot
+
+-- | Tie the LambdaHack engine client, server and frontend code
+-- with the game-specific content definitions, and run the game.
+main :: IO ()
+main = do
+  args <- getArgs
+  tieKnot args
diff --git a/GameDefinition/MainMenu.ascii b/GameDefinition/MainMenu.ascii
new file mode 100644
--- /dev/null
+++ b/GameDefinition/MainMenu.ascii
@@ -0,0 +1,26 @@
+----------------------------------------------------------------------------------
+|                                                                                |
+|                                                                                |
+|                                                                                |
+|                                                                                |
+|                                                                                |
+|                         >> Space Privateers <<                                 |
+|                                                                                |
+|                                                                                |
+|                         {{{{{{{{{{{{{{{{{{{{{{{{{                              |
+|                                                                                |
+|                         {{{{{{{{{{{{{{{{{{{{{{{{{                              |
+|                                                                                |
+|                         {{{{{{{{{{{{{{{{{{{{{{{{{                              |
+|                                                                                |
+|                         {{{{{{{{{{{{{{{{{{{{{{{{{                              |
+|                                                                                |
+|                         {{{{{{{{{{{{{{{{{{{{{{{{{                              |
+|                                                                                |
+|                                                                                |
+|                                                                                |
+|                                                                                |
+|                                                                                |
+|                                                                                |
+|                        Version X.X.X (frontend: gtk, engine: LambdaHack X.X.X) |
+----------------------------------------------------------------------------------
diff --git a/GameDefinition/PLAYING.md b/GameDefinition/PLAYING.md
new file mode 100644
--- /dev/null
+++ b/GameDefinition/PLAYING.md
@@ -0,0 +1,242 @@
+Playing Space Privateers
+========================
+
+Space Privateers is a dungeon crawler set in space. Playing the game involves
+exploring corridors of a city vessel and plundering as much loot as possible.
+
+Once the few basic command keys and on-screen symbols are learned,
+mastery and enjoyment of the game is the matter of tactical skill
+and literary imagination. To be honest, a lot of imagination is required
+for this rudimentary game, but it's playable and winnable.
+Contributions are welcome.
+
+Space Privateers uses LambdaHack engine and lot of content in the game is
+derived from it.
+
+Heroes
+------
+
+The heroes are marked on the map with symbols `@` and `1` through `9`.
+Their goal is to explore the city vessel, battle the horrors within,
+gather as much gold and gems as possible, and escape to tell the tale.
+
+The currently chosen party leader is highlighted on the screen
+and his attributes are displayed at the bottommost status line,
+which in its most complex form may look as follows.
+
+    *@12 Captain     4d1+5% Calm: 20/60 HP: 33/50 Target: merchant  [**___]
+
+The line starts with the list of party members (unless only one member
+resides on the currently displayed level) and the shortened name of the team.
+Then comes the damage of the leader's weapon (but regardless of the figures,
+each attack inflicts at least 1 damage), then his current and maximum
+Calm (composure, focus, attentiveness), then his current and maximum
+HP (hit points, health). At the end, the personal target of the leader
+is described, in this case a merchant mariner, with hit points drawn as a bar.
+
+The other status line describes the current location in relation to the party.
+
+    5  Lofty hall   [33% seen] Cursor: exact spot (71,12)  p15 l10
+
+First comes the depth of the current level and its name.
+Then the percentage of its explorable tiles already seen by the heroes.
+The 'cursor' is the common target of the whole party,
+directly manipulated with movement keys in the targeting mode.
+At the end comes the length of the shortest path from the leader
+to the cursor position and the straight-line distance between the two points.
+
+
+City vessel
+-----------
+
+The city vessel consists of 10 levels and each level consists of a large number
+of tiles. The basic tile kinds are as follows.
+
+               terrain type                       on-screen symbol
+               ground                             .
+               corridor                           #
+               wall (horizontal and vertical)     - and |
+               rock or tree                       O
+               cache                              &
+               stairs up                          <
+               stairs down                        >
+               open door                          | and -
+               closed door                        +
+               vessel hull                        blank
+
+The game world is persistent, i.e., every time the player visits a level
+during a single game, its layout is the same.
+
+
+Commands
+--------
+
+You move throughout the level using the numerical keypad (left diagram)
+or its compact laptop replacement (middle) or Vi text editor keys
+(right, also known as "Rogue-like keys", which have to be enabled
+in config.ui.ini).
+
+                7 8 9          7 8 9          y k u
+                 \|/            \|/            \|/
+                4-5-6          u-i-o          h-.-l
+                 /|\            /|\            /|\
+                1 2 3          j k l          b j n
+
+In targeting mode the keys above move the targeting cursor. In normal mode,
+`SHIFT` (or `CTRL`) and a movement key make the current party leader
+(and currently selected party members, if any) run in the indicated
+direction, until anything of interest is spotted.
+The '5', 'i' and '.' keys consume a turn and make you brace for combat,
+which reduces any damage taken for a turn and makes it impossible
+for foes to displace you. You displace enemies or friends by bumping
+into them with SHIFT (or CTRL).
+
+Melee, searching for secret doors, looting and opening closed doors
+can be done by bumping into a monster, a wall and a door, respectively.
+Few commands other than movement, 'g'etting an item from the floor,
+'a'pplying an item and 'f'linging an item are necessary for casual play.
+Some are provided only as specialized versions of more general commands
+or as building blocks for more complex convenience commands,
+e.g., the autoexplore command (key `X`) could be defined
+by the player as a macro using `BACKSPACE`, `CTRL-?`, `;` and `V`.
+
+Below are the remaining keys for terrain exploration and alteration.
+
+                keys           command
+                <              ascend a level
+                CTRL-<         ascend 10 levels
+                >              descend a level
+                CTRL->         descend 10 levels
+                ;              make one step towards the target
+                :              go to target for 100 steps
+                CTRL-:         go to target for 10 steps
+                x              explore the closest unknown spot
+                X              autoexplore 100 times
+                CTRL-X         autoexplore 10 times
+                R              rest (wait 100 times)
+                CTRL-R         rest (wait 10 times)
+                c              close door
+
+Item-use related keys are as follows.
+
+                keys           command
+                E              describe equipment of the leader
+                P              describe backpack inventory of the leader
+                S              describe the shared party stash
+                G              describe items on the ground
+                A              describe all owned items
+                g and ,        get an item
+                d              drop an item
+                e              equip an item
+                p              pack an item into inventory backpack
+                s              stash and share an item
+                a              activate applicable item
+                q              quaff potion
+                r              read scroll
+                f              fling projectable item
+                t              throw missile
+                z              zap wand
+
+To make a ranged attack, as in the last few commands above,
+you need to set your target first (however, initial target is set
+automatically as soon as a monster comes into view). Once in targeting mode,
+you can move the targeting cursor with arrow keys and switch focus
+among enemies with `*` (or among friends, projectiles and enemies, depending
+on targeting mode set by `/`). The details of the shared cursor position
+and of the personal target are described at the bottom of the screen.
+All targeting keys are listed below.
+
+                keys           command
+                KEYPAD_* and \ target enemy
+                /              cycle targeting mode
+                +              swerve targeting line
+                -              unswerve targeting line
+                BACKSPACE      clear target/cursor
+                CTRL-?         target the closest unknown spot
+                CTRL-I         target the closest item
+                CTRL-{         target the closest stairs up
+                CTRL-}         target the closest stairs down
+
+Here are the commands for automating the actions of one or more members
+of the team.
+
+                keys           command
+                =              select (or deselect) a party member
+                _              deselect (or select) all on the level
+                v              voice again the recorded commands
+                V              voice the recorded commands 100 times
+                CTRL-v         voice the recorded commands 1000 times
+                CTRL-V         voice the recorded commands 10 times
+                '              start recording commands
+                CTRL-A         automate faction (ESC to retake control)
+
+Assorted remaining keys and commands follow.
+
+                keys           command
+                ?              display help
+                D              display player diary
+                T              mark suspect terrain
+                Z              mark visible zone
+                C              mark smell clues
+                TAB            cycle among party members on the level
+                SHIFT-TAB      cycle among all party members
+                SPACE          clear messages
+                ESC            cancel action, open Main Menu
+                RET            accept choice
+                0--6           pick a new hero leader anywhere in the dungeon
+
+Commands for saving and exiting the current game, starting a new game, etc.,
+are listed in the Main Menu, brought up by the `ESC` key.
+Game difficulty setting affects hitpoints at birth for any actors
+of any UI-using faction. For a person new to roguelikes, the Duel game mode
+offers a gentle introduction. The subsequent game modes gradually introduce
+squad combat, stealth, asymmetric battles and more game elements.
+
+                keys           command
+                CTRL-x         save and exit
+                CTRL-u         new Duel game
+                CTRL-k         new Skirmish game
+                CTRL-m         new Ambush game
+                CTRL-b         new Battle game
+                CTRL-a         new Campaign game
+                CTRL-d         cycle next game difficulty
+
+Monsters
+--------
+
+Heroes are not alone in the city vessel. Monstrosities, natural
+and out of this world, roam the dark caves and crawl from damp holes
+day and night. While heroes pay attention to all other party members
+and take care to move one at a time, monsters don't care about each other
+and all move at once, sometimes brutally colliding by accident.
+
+When the hero bumps into a monster or a monster attacks the hero,
+melee combat occurs. The best equipped weapon or the best fighting organ
+of each opponent is taken into account for calculating damage.
+The damage the current hero can potentially inflict is displayed
+at the bottom of the screen, but the actual damage depends also
+on the monster's armor. Heroes and monsters running into one another
+(with the `SHIFT` key) do not inflict damage, but change places.
+This gives the opponent a free blow, but can improve the tactical situation
+or aid escape.
+
+Slinging a missile at a target wounds it, consuming the weapon in the process.
+You may propel any item in your equipment, inventory and on the ground
+(press `?` to choose an item and press it again for a non-standard choice).
+Only items of a few kinds inflict any damage, but some have other effects.
+Whenever the monster's or hero's hit points reach zero, the combatant dies.
+When the last hero dies, the game ends in defeat.
+
+
+On Winning and Dying
+--------------------
+
+You win the game if you escape the city vessel alive. Your score is based on the
+gold and precious gems you've plundered, plus a bonus based on the number of
+heroes you lost.
+
+If all your heroes fall, you are awarded a score for your valiant deeds,
+but no winning bonus. When, invariably, a new overconfident party
+of adventurers storms the dungeon, they start from a new entrance,
+with no experience and no equipment, and new, undaunted enemies
+bar their way.
diff --git a/GameDefinition/TieKnot.hs b/GameDefinition/TieKnot.hs
new file mode 100644
--- /dev/null
+++ b/GameDefinition/TieKnot.hs
@@ -0,0 +1,38 @@
+-- | Here the knot of engine code pieces and the game-specific
+-- content definitions is tied, resulting in an executable game.
+module TieKnot ( tieKnot ) where
+
+import qualified Client.UI.Content.KeyKind as Content.KeyKind
+import qualified Content.CaveKind
+import qualified Content.FactionKind
+import qualified Content.ItemKind
+import qualified Content.ModeKind
+import qualified Content.PlaceKind
+import qualified Content.RuleKind
+import qualified Content.TileKind
+import Game.LambdaHack.Client (exeFrontend)
+import qualified Game.LambdaHack.Common.Kind as Kind
+import Game.LambdaHack.SampleImplementation.SampleMonadClient (executorCli)
+import Game.LambdaHack.SampleImplementation.SampleMonadServer (executorSer)
+import Game.LambdaHack.Server (mainSer)
+
+-- | Tie the LambdaHack engine client, server and frontend code
+-- with the game-specific content definitions, and run the game.
+tieKnot :: [String] -> IO ()
+tieKnot args =
+  let -- Common content operations, created from content definitions.
+      copsServer = Kind.COps
+        { cocave    = Kind.createOps Content.CaveKind.cdefs
+        , cofaction = Kind.createOps Content.FactionKind.cdefs
+        , coitem    = Kind.createOps Content.ItemKind.cdefs
+        , comode    = Kind.createOps Content.ModeKind.cdefs
+        , coplace   = Kind.createOps Content.PlaceKind.cdefs
+        , corule    = Kind.createOps Content.RuleKind.cdefs
+        , cotile    = Kind.createOps Content.TileKind.cdefs
+        }
+      -- Client content operations.
+      copsClient = Content.KeyKind.standardKeys
+      -- A single frontend is currently started by the server,
+      -- instead of each client starting it's own.
+      startupFrontend = exeFrontend executorCli executorCli copsClient
+  in mainSer args copsServer executorSer startupFrontend
diff --git a/GameDefinition/config.ui.default b/GameDefinition/config.ui.default
new file mode 100644
--- /dev/null
+++ b/GameDefinition/config.ui.default
@@ -0,0 +1,28 @@
+; ; This is a commented out copy of the default UI settings config file
+; ; that is embedded in the binary.
+; ; A user config file can overrides these options. The game looks for it at
+; ; ~/.SpacePrivateers/config.ui.ini (or a similar path, depending on the OS).
+; ; Warning: options are case-sensitive and only ';' for comments is permitted.
+
+; [extra_commands]
+; ; A handy shorthand with Vi keys:
+; Macro_1 = ("comma", ([CmdItem], Macro "" ["g"]))
+
+; [hero_names]
+; HeroName_0 = ("Regulus Secundinus", "he")
+; HeroName_1 = ("Sabina Petronia", "she")
+; HeroName_2 = ("Titus Severus", "he")
+; HeroName_3 = ("Blandina Domitius", "she")
+; HeroName_4 = ("Lucretius Caelius", "he")
+; HeroName_5 = ("Tacita Aurelia", "she")
+; HeroName_6 = ("Vitus Antoninus", "he")
+
+; [ui]
+; movementViKeys_hjklyubn = False
+; movementLaptopKeys_uk8o79jl = True
+; font = "Terminus,Monospace normal normal normal normal 12"
+; historyMax = 5000
+; maxFps = 15
+; noAnim = False
+; runStopMsgs = False
+[dummy]
diff --git a/GameDefinition/scores b/GameDefinition/scores
new file mode 100644
Binary files /dev/null and b/GameDefinition/scores differ
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,32 @@
+Copyright (c) 2008--2014 Andres Loeh
+Copyright (c) 2010--2014 Mikolaj Konarski
+Copyright (c) 2014, Tuukka Turto
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Tuukka Turto nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,8 @@
+Space Privateers is a adventure game set in far future. Player plays a space
+privateer who has recently docked with a merchant city vessel and plans to
+plunder some of the loot before escaping. The task will not be easy and there
+are many dangers awaiting in the deep corridors and rooms of the city vessel.
+
+Space Privateers uses [LambdaHack] [1] engine.
+
+[1]: https://github.com/LambdaHack/LambdaHack/
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/SpacePrivateers.cabal b/SpacePrivateers.cabal
new file mode 100644
--- /dev/null
+++ b/SpacePrivateers.cabal
@@ -0,0 +1,56 @@
+name:                 SpacePrivateers
+version:              0.1.0.0
+synopsis:             Simple space pirate roguelike
+description:          Space Privateers is a roguelike game set in space.
+homepage:             https://github.com/tuturto/space-privateers
+license:              BSD3
+license-file:         LICENSE
+author:               Tuukka Turto
+maintainer:           tuukka.turto@oktaeder.net
+category:             Game
+build-type:           Simple
+data-files:           GameDefinition/config.ui.default,
+                      GameDefinition/scores
+extra-source-files:   GameDefinition/PLAYING.md, GameDefinition/MainMenu.ascii,
+                      README.md, LICENSE, CREDITS, changelog
+cabal-version:        >= 1.10
+source-repository head
+  type:               git
+  location:           git://github.com/tuturto/space-privateers.git
+
+executable SpacePrivateers
+  hs-source-dirs:     GameDefinition
+  main-is:            Main.hs
+  other-modules:      Client.UI.Content.KeyKind,
+                      Content.CaveKind,
+                      Content.FactionKind,
+                      Content.ItemKind,
+                      Content.ItemKindActor,
+                      Content.ItemKindOrgan,
+                      Content.ItemKindShrapnel,
+                      Content.ModeKind,
+                      Content.PlaceKind,
+                      Content.RuleKind,
+                      Content.TileKind,
+                      TieKnot,
+                      Paths_SpacePrivateers
+
+  build-depends:      base              >=4.7       && <4.8,
+                      LambdaHack        >= 0.2.14   && < 0.2.15,
+                      containers        >= 0.5      && < 1,
+                      enummapset-th     >= 0.6.0.0  && < 1,
+                      filepath          >= 1.2.0.1  && < 2,
+                      template-haskell  >= 2.6      && < 3,
+                      text              >= 0.11.2.3 && < 2
+  default-language:   Haskell2010
+
+  default-extensions: MonoLocalBinds, ScopedTypeVariables, OverloadedStrings
+                      BangPatterns, RecordWildCards, NamedFieldPuns
+  other-extensions:   TemplateHaskell
+  ghc-options: -Wall -fwarn-orphans -fwarn-tabs -fwarn-incomplete-uni-patterns
+  ghc-options: -fwarn-incomplete-record-updates -fwarn-monomorphism-restriction
+  ghc-options: -fwarn-unrecognised-pragmas
+  ghc-options: -fno-warn-auto-orphans -fno-warn-implicit-prelude
+  ghc-options: -fno-ignore-asserts -funbox-strict-fields
+  ghc-options: -threaded -with-rtsopts=-C0.005
+
diff --git a/changelog b/changelog
new file mode 100644
--- /dev/null
+++ b/changelog
@@ -0,0 +1,2 @@
+v0.1.0
+        * Initial version
