packages feed

Allure 0.9.5.0 → 0.10.2.0

raw patch · 45 files changed

+12662/−8483 lines, 45 filesdep +containersdep +file-embeddep +hsinidep −randomdep ~LambdaHackdep ~template-haskellbinary-addedPVP ok

version bump matches the API change (PVP)

Dependencies added: containers, file-embed, hsini, splitmix, tasty, tasty-hunit, th-lift-instances

Dependencies removed: random

Dependency ranges changed: LambdaHack, template-haskell

API changes (from Hackage documentation)

- Client.UI.Content.Input: closeDoorTriggers :: [TriggerTile]
+ Content.CaveKind: groupNames :: [GroupName CaveKind]
+ Content.CaveKind: groupNamesSingleton :: [GroupName CaveKind]
+ Content.CaveKind: pattern CAVE_AMBUSH :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_ARENA :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_BATTLE :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_BRAWL :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_BRAWL_ALT :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_BRIDGE :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_CASINO :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_ESCAPE :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_EXIT :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_HUNT :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_LABORATORY :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_MUSEUM :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_NOISE :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_OUTERMOST :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_POWER :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_RAID :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_RESIDENTIAL :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_ROGUE :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_SAFARI_1 :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_SAFARI_2 :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_SAFARI_3 :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_SHALLOW_ROGUE :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_SHOOTOUT :: GroupName CaveKind
+ Content.CaveKind: pattern CAVE_ZOO :: GroupName CaveKind
+ Content.ItemKind: groupNames :: [GroupName ItemKind]
+ Content.ItemKind: groupNamesSingleton :: [GroupName ItemKind]
+ Content.ItemKind: pattern ARMOR_LOOSE :: GroupName ItemKind
+ Content.ItemKind: pattern CLOTHING_MISC :: GroupName ItemKind
+ Content.ItemKind: pattern COOKED_PLANT :: GroupName ItemKind
+ Content.ItemKind: pattern GARDENING_TOOL :: GroupName ItemKind
+ Content.ItemKind: pattern HARPOON :: GroupName ItemKind
+ Content.ItemKind: pattern LIQUID_NITROGEN :: GroupName ItemKind
+ Content.ItemKindActor: actorsGN :: [GroupName ItemKind]
+ Content.ItemKindActor: actorsGNSingleton :: [GroupName ItemKind]
+ Content.ItemKindActor: pattern ADD_NOCTO_1 :: GroupName ItemKind
+ Content.ItemKindActor: pattern ADD_SIGHT :: GroupName ItemKind
+ Content.ItemKindActor: pattern AMBUSHER_HERO :: GroupName ItemKind
+ Content.ItemKindActor: pattern ANIMAL :: GroupName ItemKind
+ Content.ItemKindActor: pattern ANY_ARROW :: GroupName ItemKind
+ Content.ItemKindActor: pattern AQUATIC_ANIMAL :: GroupName ItemKind
+ Content.ItemKindActor: pattern AQUATIC_MONSTER :: GroupName ItemKind
+ Content.ItemKindActor: pattern ARMOR_RANGED :: GroupName ItemKind
+ Content.ItemKindActor: pattern BLOWTORCH :: GroupName ItemKind
+ Content.ItemKindActor: pattern BONDING_TOOL :: GroupName ItemKind
+ Content.ItemKindActor: pattern BRAWLER_HERO :: GroupName ItemKind
+ Content.ItemKindActor: pattern BREACHING_TOOL :: GroupName ItemKind
+ Content.ItemKindActor: pattern CAN_OF_STICKY_FOAM :: GroupName ItemKind
+ Content.ItemKindActor: pattern CIVILIAN :: GroupName ItemKind
+ Content.ItemKindActor: pattern CONSTRUCTION_HOOTER :: GroupName ItemKind
+ Content.ItemKindActor: pattern CONSTRUCTION_ROBOT :: GroupName ItemKind
+ Content.ItemKindActor: pattern COOKED_FOOD :: GroupName ItemKind
+ Content.ItemKindActor: pattern CRAWL_HERO :: GroupName ItemKind
+ Content.ItemKindActor: pattern ESCAPIST_HERO :: GroupName ItemKind
+ Content.ItemKindActor: pattern EXPLOSIVE_MONSTER :: GroupName ItemKind
+ Content.ItemKindActor: pattern FIREPROOF_CLOTH :: GroupName ItemKind
+ Content.ItemKindActor: pattern GEM :: GroupName ItemKind
+ Content.ItemKindActor: pattern HERO :: GroupName ItemKind
+ Content.ItemKindActor: pattern IMMOBILE_ANIMAL :: GroupName ItemKind
+ Content.ItemKindActor: pattern IMMOBILE_ROBOT :: GroupName ItemKind
+ Content.ItemKindActor: pattern LIGHT_ATTENUATOR :: GroupName ItemKind
+ Content.ItemKindActor: pattern MERCENARY_AMMO :: GroupName ItemKind
+ Content.ItemKindActor: pattern MERCENARY_HERO :: GroupName ItemKind
+ Content.ItemKindActor: pattern MERCENARY_WEAPON :: GroupName ItemKind
+ Content.ItemKindActor: pattern MOBILE_ANIMAL :: GroupName ItemKind
+ Content.ItemKindActor: pattern MOBILE_MONSTER :: GroupName ItemKind
+ Content.ItemKindActor: pattern MOBILE_ROBOT :: GroupName ItemKind
+ Content.ItemKindActor: pattern MONSTER :: GroupName ItemKind
+ Content.ItemKindActor: pattern NEEDLE :: GroupName ItemKind
+ Content.ItemKindActor: pattern POLE :: GroupName ItemKind
+ Content.ItemKindActor: pattern POLE_OR_HANDLE :: GroupName ItemKind
+ Content.ItemKindActor: pattern RANGER_HERO :: GroupName ItemKind
+ Content.ItemKindActor: pattern RAW_MEAT_CHUNK :: GroupName ItemKind
+ Content.ItemKindActor: pattern RING_OF_OPPORTUNITY_SNIPER :: GroupName ItemKind
+ Content.ItemKindActor: pattern ROASTED_MEAT_CHUNK :: GroupName ItemKind
+ Content.ItemKindActor: pattern ROBOT :: GroupName ItemKind
+ Content.ItemKindActor: pattern SCOUT_HERO :: GroupName ItemKind
+ Content.ItemKindActor: pattern SCOUT_MONSTER :: GroupName ItemKind
+ Content.ItemKindActor: pattern SHARPENING_TOOL :: GroupName ItemKind
+ Content.ItemKindActor: pattern SOLDIER_HERO :: GroupName ItemKind
+ Content.ItemKindActor: pattern SPOTLIGHT :: GroupName ItemKind
+ Content.ItemKindActor: pattern STARTING_ARMOR :: GroupName ItemKind
+ Content.ItemKindActor: pattern STARTING_WEAPON :: GroupName ItemKind
+ Content.ItemKindActor: pattern S_BULLTEPROOF_VEST :: GroupName ItemKind
+ Content.ItemKindActor: pattern S_EMPTY_FLASK :: GroupName ItemKind
+ Content.ItemKindActor: pattern S_PERFUME_POTION :: GroupName ItemKind
+ Content.ItemKindActor: pattern S_WOODEN_TORCH :: GroupName ItemKind
+ Content.ItemKindActor: pattern TRANQUILIZER_DART :: GroupName ItemKind
+ Content.ItemKindActor: pattern WASTE_CONTAINER :: GroupName ItemKind
+ Content.ItemKindActor: pattern WEAK_ARROW :: GroupName ItemKind
+ Content.ItemKindActor: pattern WIRECUTTING_TOOL :: GroupName ItemKind
+ Content.ItemKindBlast: blastBonusStatOf :: GroupName ItemKind -> GroupName ItemKind
+ Content.ItemKindBlast: blastNoStatOf :: GroupName ItemKind -> GroupName ItemKind
+ Content.ItemKindBlast: blastsGN :: [GroupName ItemKind]
+ Content.ItemKindBlast: blastsGNSingleton :: [GroupName ItemKind]
+ Content.ItemKindBlast: pattern ADVERTISEMENT :: GroupName ItemKind
+ Content.ItemKindBlast: pattern ARMOR_MISC :: GroupName ItemKind
+ Content.ItemKindBlast: pattern BLAST_SOURCE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern COLD_SOURCE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern FIRE_SOURCE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern OIL_SOURCE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern STORY_TELLING :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_ANTIDOTE_MIST :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_ANTI_SLOW_MIST :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_BOILING_WATER :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_BURNING_OIL_2 :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_BURNING_OIL_3 :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_BURNING_OIL_4 :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_CALMING_MIST :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_CONCUSSION :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_CURRENT_DISCHARGE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_CURRENT_RECHARGE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_DEFENSELESSNESS_RUNOUT :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_DENSE_SHOWER :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_DISTORTION :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_DISTRESSING_ODOR :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_EYE_DROP :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_EYE_SHINE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_FIRECRACKER :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_FOCUSED_BURNING_OIL_2 :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_FOCUSED_BURNING_OIL_3 :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_FOCUSED_BURNING_OIL_4 :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_FOCUSED_CONCUSSION :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_FOCUSED_FLASH :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_FOCUSED_FRAGMENTATION :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_FOCUSED_GLASS_HAIL :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_FOCUSED_SLOWNESS_MIST :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_FRAGMENTATION :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_GLASS_HAIL :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_GLUE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_HASTE_SPRAY :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_HEALING_MIST :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_HEALING_MIST_2 :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_IRON_FILING :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_MELEE_PROTECTIVE_BALM :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_PAINT_DROPLET :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_PHEROMONE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_PING_PLASH :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_POISON_CLOUD :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_RANGE_PROTECTIVE_BALM :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_RESOLUTION_DUST :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_RHINO_HOLOGRAM :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_SLEEP_MIST :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_SLOWNESS_MIST :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_SMELLY_DROPLET :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_SMOKE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_SPARSE_SHOWER :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_VIOLENT_BURNING_OIL_2 :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_VIOLENT_BURNING_OIL_3 :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_VIOLENT_BURNING_OIL_4 :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_VIOLENT_CONCUSSION :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_VIOLENT_FLASH :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_VIOLENT_FRAGMENTATION :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_VIOLENT_SLOWNESS_MIST :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_WASTE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_WHISKEY_SPRAY :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_WOUNDING_MIST :: GroupName ItemKind
+ Content.ItemKindBlast: pattern S_YOUTH_SPRINKLE :: GroupName ItemKind
+ Content.ItemKindBlast: pattern WATER_SOURCE :: GroupName ItemKind
+ Content.ItemKindEmbed: embedsGN :: [GroupName ItemKind]
+ Content.ItemKindEmbed: embedsGNSingleton :: [GroupName ItemKind]
+ Content.ItemKindEmbed: pattern ABANDONED_CACHE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern BARREL_CONTENTS :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern BIG_FIRE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern BLACK_STARRY_SKY :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern BLOOD_ON_WALL :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern CLOTH_RAG :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern CRACKED_FLUE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern DECONTAMINATION_CHAMBER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern DEPOSIT_BOX :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern DISENGAGED_DOCKING_GEAR :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern DISPLAY_3D :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern DOORWAY_TRAP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern DOORWAY_TRAP_UNKNOWN :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern DOOR_TRAP_PUSH :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern EDIBLE_PLANT :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern EDIBLE_PLANT_RIPE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern ESCAPE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern FIRE_FIGHTING_GEAR :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern FIRE_FIGHTING_ITEM :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern FROST :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern FROZEN_GROUND :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern HANDLE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern HANDLE_AND_STEEL :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern JEWELRY_CASE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern JEWELRY_DISPLAY_TRAP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern LECTERN :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern LIFT_DOWN :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern LIFT_TRAP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern LIFT_UP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern MUSEAL :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern OBSCENE_PICTOGRAM :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern OIL_PUDDLE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern PERFUME :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern POLE_AND_STEEL :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern RUBBLE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern RUINED_FIRST_AID_KIT :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern SCRATCH_ON_WALL :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern SHALLOW_WATER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern SHUTTLE_HARDWARE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern SIGNAGE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern SMALL_FIRE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern SMALL_FIRE_5 :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern SPACESUIT_PART :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern STAIRS_DOWN :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern STAIRS_DOWN_OUTDOOR :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern STAIRS_TRAP_DOWN :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern STAIRS_TRAP_DOWN_OIL :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern STAIRS_TRAP_UP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern STAIRS_UP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern STAIRS_UP_OUTDOOR :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern STARTING_HAMMER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern STEEL_SCRAP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern STRAIGHT_PATH :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern SUBTLE_FRESCO :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_CLEAVER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_COOKED_BARK :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_COOKED_BERRY :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_COOKED_FLOWER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_COOKED_FRUIT :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_COOKED_FUNGUS :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_COOKED_HERB :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_COOKED_LEAF :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_COOKED_PUMPKIN :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_CROWBAR :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_DAGGER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_DOUSED_OIL_LAMP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_DOUSED_WOODEN_TORCH :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_DULL_FLOWER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_ENCHANCED_BERRY :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_FIRE_AXE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_FRAGRANT_HERB :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_FRAYED_FUNGUS :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_HALBERD_BLUNT :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_HALBERD_SHARP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_HARPOON_CARGO :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_HARPOON_SHARP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_LONG_BLUNT_HAMMER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_LONG_CLUB :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_LONG_SHARP_HAMMER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_LONG_SPEAR :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_OIL_LAMP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_PIPE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_POLE_CLEAVER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_POLL_AXE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_PUMPKIN :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_RAPIER_BLUNT :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_RAPIER_SHARP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_RECONFIGURED_FRUIT :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_REFRIGERATION_COIL :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_ROSE_WATER_FLASK :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SANDSTONE_ROCK :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SHARPENED_PIPE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SHIELD_BLUNT :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SHIELD_SHARP :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SHORT_BLUNT_HAMMER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SHORT_CLUB :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SHORT_SHARP_HAMMER :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SPACESUIT :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SPACESUIT_BOOT :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SPACESUIT_GLOVE :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SPACESUIT_HELMET :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SPACESUIT_JACKET :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SPACESUIT_TORN :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SPACESUIT_TROUSERS :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_SPICY_BARK :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_STAFF :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_THIC_LEAF :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern S_WATER_FLASK :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern THICK_CLOTH :: GroupName ItemKind
+ Content.ItemKindEmbed: pattern WORKSHOP_BENCH :: GroupName ItemKind
+ Content.ItemKindOrgan: organsGN :: [GroupName ItemKind]
+ Content.ItemKindOrgan: organsGNSingleton :: [GroupName ItemKind]
+ Content.ItemKindOrgan: pattern ALCOHOL :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern BACKSTORY :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern BACKSTORY_BAD :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern BACKSTORY_FLUFF :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern BACKSTORY_GOOD :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern BACKSTORY_MIXED :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern BACKSTORY_NEUTRAL :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern ELECTRIC_AMBIENCE :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern GENETIC_FLAW :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern GENETIC_FLAW_10 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern GENETIC_FLAW_3 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern SCAVENGER :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_ANIMAL_BRAIN :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_ANIMAL_STOMACH :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_ANTLER :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_ARMORED_SKIN :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_BARK :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_BEE_STING :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_BOILING_FISSURE :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_BOILING_VENT :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_COILED_TAIL :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_COOLING_FISSURE :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_COOLING_VENT :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_CRUDE_WELD :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_DUST_FISSURE :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_DUST_VENT :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_EAR_3 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_EAR_6 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_EAR_8 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_EYE_3 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_EYE_6 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_EYE_8 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_FIST :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_FLOTATION_BAG :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_FOOT :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_FUEL_FISSURE :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_FUEL_VENT :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_HOOKED_CLAW :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_HORN :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_HUGE_TAIL :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_HULL_PLATING :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_HUNGRY :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_INK_SAC :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_INSECT_MORTALITY :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_JAW :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_JET_BOOSTER :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_LARGE_JAW :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_LARGE_TAIL :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_LIP :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_LIVE_WIRE :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_MEDBOT_FISSURE :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_MEDBOT_VENT :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_MOUTH_VENT :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_NOSTRIL :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_POWERFUL_HIND_LEGS :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_RATLLE :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_RAZOR :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_RHINO_HORN :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_RHINO_INERTIA :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_ROBOT_BRAIN :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_SAPIENT_BRAIN :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_SCENT_GLAND :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_SCREECHING_BEAK :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_SMALL_BEAK :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_SMALL_CLAW :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_SMALL_JAW :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_SNOUT :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_SPEED_GLAND_10 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_SPEED_GLAND_5 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_STING :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_TENTACLE :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_THORN :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_TIP :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_VENOM_FANG :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_VENOM_TOOTH :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_VISION_12 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_VISION_16 :: GroupName ItemKind
+ Content.ItemKindOrgan: pattern S_VISION_6 :: GroupName ItemKind
+ Content.ItemKindTemporary: bonusStatGN :: [GroupName ItemKind]
+ Content.ItemKindTemporary: noStatGN :: [GroupName ItemKind]
+ Content.ItemKindTemporary: pattern S_BLIND :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_DEAF :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_DEAFENED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_DEFENSELESS :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_DISPOSSESSED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_DRUNK :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_FAR_SIGHTED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_FOUL_SMELLING :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_FRENZIED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_HASTED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_IMMOBILE :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_IMPATIENT :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_IRREPLACEABLE :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_KEEN_SMELLING :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_MELEE_DEFLECTING :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_MORE_COMBATIVE :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_MORE_DISPLACING :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_MORE_MOBILE :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_MORE_MODIFYING :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_MORE_PATIENT :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_MORE_PRACTICAL :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_MORE_PROJECTING :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_MORE_TIDY :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_PACIFIED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_PAINTED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_PARSIMONIOUS :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_POISONED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_POISON_RESISTANT :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_PROTECTED_FROM_MELEE :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_PROTECTED_FROM_RANGED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_RANGED_DEFLECTING :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_REGENERATING :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_RESOLUTE :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_RETAINING :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_ROSE_SMELLING :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_SHINY_EYED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_SLOWED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_SLOW_RESISTANT :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_STRENGTHENED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_WEAKENED :: GroupName ItemKind
+ Content.ItemKindTemporary: pattern S_WITHHOLDING :: GroupName ItemKind
+ Content.ItemKindTemporary: temporariesGNSingleton :: [GroupName ItemKind]
+ Content.ModeKind: groupNames :: [GroupName ModeKind]
+ Content.ModeKind: groupNamesSingleton :: [GroupName ModeKind]
+ Content.ModeKind: pattern AMBUSH :: GroupName ModeKind
+ Content.ModeKind: pattern BATTLE :: GroupName ModeKind
+ Content.ModeKind: pattern BATTLE_DEFENSE :: GroupName ModeKind
+ Content.ModeKind: pattern BATTLE_SURVIVAL :: GroupName ModeKind
+ Content.ModeKind: pattern BRAWL :: GroupName ModeKind
+ Content.ModeKind: pattern BURNING :: GroupName ModeKind
+ Content.ModeKind: pattern CRAWL :: GroupName ModeKind
+ Content.ModeKind: pattern CRAWL_EMPTY :: GroupName ModeKind
+ Content.ModeKind: pattern CRAWL_SURVIVAL :: GroupName ModeKind
+ Content.ModeKind: pattern DEFENSE :: GroupName ModeKind
+ Content.ModeKind: pattern DEFENSE_EMPTY :: GroupName ModeKind
+ Content.ModeKind: pattern DIG :: GroupName ModeKind
+ Content.ModeKind: pattern ESCAPE :: GroupName ModeKind
+ Content.ModeKind: pattern FOGGY :: GroupName ModeKind
+ Content.ModeKind: pattern HUNT :: GroupName ModeKind
+ Content.ModeKind: pattern LONG :: GroupName ModeKind
+ Content.ModeKind: pattern NIGHT :: GroupName ModeKind
+ Content.ModeKind: pattern PERILOUS :: GroupName ModeKind
+ Content.ModeKind: pattern RAID :: GroupName ModeKind
+ Content.ModeKind: pattern RANGED :: GroupName ModeKind
+ Content.ModeKind: pattern SAFARI :: GroupName ModeKind
+ Content.ModeKind: pattern SAFARI_SURVIVAL :: GroupName ModeKind
+ Content.ModeKind: pattern SEE :: GroupName ModeKind
+ Content.ModeKind: pattern SHOOTOUT :: GroupName ModeKind
+ Content.ModeKind: pattern SHORT :: GroupName ModeKind
+ Content.ModeKind: pattern ZOO :: GroupName ModeKind
+ Content.PlaceKind: groupNames :: [GroupName PlaceKind]
+ Content.PlaceKind: groupNamesSingleton :: [GroupName PlaceKind]
+ Content.PlaceKind: pattern AMBUSH :: GroupName PlaceKind
+ Content.PlaceKind: pattern ARENA :: GroupName PlaceKind
+ Content.PlaceKind: pattern BATTLE :: GroupName PlaceKind
+ Content.PlaceKind: pattern BRAWL :: GroupName PlaceKind
+ Content.PlaceKind: pattern CLOSED_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern CLOSED_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern DECON_OPEN_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern DECON_OPEN_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern DECON_TINY_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern DECON_TINY_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern DECON_WALLED_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern DECON_WALLED_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern EMPTY :: GroupName PlaceKind
+ Content.PlaceKind: pattern ESCAPE :: GroupName PlaceKind
+ Content.PlaceKind: pattern ESCAPE_FROM_SPACESHIP_DOWN :: GroupName PlaceKind
+ Content.PlaceKind: pattern EXIT :: GroupName PlaceKind
+ Content.PlaceKind: pattern GATED_CLOSED_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern GATED_CLOSED_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern GATED_OPEN_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern GATED_OPEN_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern GATED_TINY_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern GATED_TINY_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern INDOOR_ESCAPE_DOWN :: GroupName PlaceKind
+ Content.PlaceKind: pattern INDOOR_ESCAPE_UP :: GroupName PlaceKind
+ Content.PlaceKind: pattern LABORATORY :: GroupName PlaceKind
+ Content.PlaceKind: pattern MUSEUM :: GroupName PlaceKind
+ Content.PlaceKind: pattern NOISE :: GroupName PlaceKind
+ Content.PlaceKind: pattern OPEN_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern OPEN_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern OUTDOOR_CLOSED_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern OUTDOOR_ESCAPE_DOWN :: GroupName PlaceKind
+ Content.PlaceKind: pattern OUTDOOR_TINY_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern OUTDOOR_WALLED_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern RAID :: GroupName PlaceKind
+ Content.PlaceKind: pattern RESIDENTIAL :: GroupName PlaceKind
+ Content.PlaceKind: pattern ROGUE :: GroupName PlaceKind
+ Content.PlaceKind: pattern SHOOTOUT :: GroupName PlaceKind
+ Content.PlaceKind: pattern TINY_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern TINY_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern WALLED_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern WALLED_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern WELDED_OPEN_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern WELDED_OPEN_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern WELDED_TINY_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern WELDED_TINY_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern WELDED_WALLED_LIFT :: GroupName PlaceKind
+ Content.PlaceKind: pattern WELDED_WALLED_STAIRCASE :: GroupName PlaceKind
+ Content.PlaceKind: pattern ZOO :: GroupName PlaceKind
+ Content.TileKind: groupNames :: [GroupName TileKind]
+ Content.TileKind: groupNamesSingleton :: [GroupName TileKind]
+ Content.TileKind: pattern AIRLOCK_FENCE :: GroupName TileKind
+ Content.TileKind: pattern AMBUSH_SET_DARK :: GroupName TileKind
+ Content.TileKind: pattern AMBUSH_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern ARENA_SET_DARK :: GroupName TileKind
+ Content.TileKind: pattern ARENA_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern ASHES_SMOKE_DARK :: GroupName TileKind
+ Content.TileKind: pattern ASHES_SMOKE_LIT :: GroupName TileKind
+ Content.TileKind: pattern BARREL :: GroupName TileKind
+ Content.TileKind: pattern BATTLE_SET_DARK :: GroupName TileKind
+ Content.TileKind: pattern BATTLE_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern BRAWL_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern BUSH_CLUMP_DARK :: GroupName TileKind
+ Content.TileKind: pattern BUSH_CLUMP_LIT :: GroupName TileKind
+ Content.TileKind: pattern BUSH_GROVE_DARK :: GroupName TileKind
+ Content.TileKind: pattern BUSH_GROVE_LIT :: GroupName TileKind
+ Content.TileKind: pattern CACHE_DEPOSIT :: GroupName TileKind
+ Content.TileKind: pattern CACHE_JEWELRY :: GroupName TileKind
+ Content.TileKind: pattern CACHE_MAZE :: GroupName TileKind
+ Content.TileKind: pattern CACHE_SHUTTLE :: GroupName TileKind
+ Content.TileKind: pattern DAMP_FLOOR_DARK :: GroupName TileKind
+ Content.TileKind: pattern DAMP_FLOOR_LIT :: GroupName TileKind
+ Content.TileKind: pattern DECON_LIFT_DOWN :: GroupName TileKind
+ Content.TileKind: pattern DECON_LIFT_UP :: GroupName TileKind
+ Content.TileKind: pattern DECON_STAIRCASE_DOWN :: GroupName TileKind
+ Content.TileKind: pattern DECON_STAIRCASE_UP :: GroupName TileKind
+ Content.TileKind: pattern DIRT_DARK :: GroupName TileKind
+ Content.TileKind: pattern DIRT_LIT :: GroupName TileKind
+ Content.TileKind: pattern DOORLESS_MACHINERY :: GroupName TileKind
+ Content.TileKind: pattern DOORLESS_WALL :: GroupName TileKind
+ Content.TileKind: pattern EMPTY_AIRLOCK_FENCE :: GroupName TileKind
+ Content.TileKind: pattern EMPTY_SET_DARK :: GroupName TileKind
+ Content.TileKind: pattern EMPTY_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern ESCAPE_DOWN :: GroupName TileKind
+ Content.TileKind: pattern ESCAPE_OUTDOOR_DOWN :: GroupName TileKind
+ Content.TileKind: pattern ESCAPE_SET_DARK :: GroupName TileKind
+ Content.TileKind: pattern ESCAPE_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern ESCAPE_SPACESHIP_DOWN :: GroupName TileKind
+ Content.TileKind: pattern ESCAPE_UP :: GroupName TileKind
+ Content.TileKind: pattern EXIT_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern FILLER_WALL :: GroupName TileKind
+ Content.TileKind: pattern FLOOR_ACTOR_ITEM :: GroupName TileKind
+ Content.TileKind: pattern FLOOR_ACTOR_ITEM_LIT :: GroupName TileKind
+ Content.TileKind: pattern FLOOR_ARENA_DARK :: GroupName TileKind
+ Content.TileKind: pattern FLOOR_ARENA_LIT :: GroupName TileKind
+ Content.TileKind: pattern FLOOR_CORRIDOR_DARK :: GroupName TileKind
+ Content.TileKind: pattern FLOOR_CORRIDOR_LIT :: GroupName TileKind
+ Content.TileKind: pattern FOG_CLUMP_DARK :: GroupName TileKind
+ Content.TileKind: pattern FOG_CLUMP_LIT :: GroupName TileKind
+ Content.TileKind: pattern GATED_LIFT_DOWN :: GroupName TileKind
+ Content.TileKind: pattern GATED_LIFT_UP :: GroupName TileKind
+ Content.TileKind: pattern GATED_STAIRCASE_DOWN :: GroupName TileKind
+ Content.TileKind: pattern GATED_STAIRCASE_UP :: GroupName TileKind
+ Content.TileKind: pattern HABITAT_CONTAINMENT_WALL :: GroupName TileKind
+ Content.TileKind: pattern HUNT_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern LAB_TRAIL_LIT :: GroupName TileKind
+ Content.TileKind: pattern LIFT_TERMINAL_DARK :: GroupName TileKind
+ Content.TileKind: pattern LIFT_TERMINAL_LIT :: GroupName TileKind
+ Content.TileKind: pattern MUSEUM_SET_DARK :: GroupName TileKind
+ Content.TileKind: pattern MUSEUM_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern NOISE_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern OILY_FLOOR_DARK :: GroupName TileKind
+ Content.TileKind: pattern OILY_FLOOR_LIT :: GroupName TileKind
+ Content.TileKind: pattern OIL_RESIDUE_DARK :: GroupName TileKind
+ Content.TileKind: pattern OIL_RESIDUE_LIT :: GroupName TileKind
+ Content.TileKind: pattern OPENABLE_WALL :: GroupName TileKind
+ Content.TileKind: pattern ORDINARY_LIFT_DOWN :: GroupName TileKind
+ Content.TileKind: pattern ORDINARY_LIFT_UP :: GroupName TileKind
+ Content.TileKind: pattern ORDINARY_STAIRCASE_DOWN :: GroupName TileKind
+ Content.TileKind: pattern ORDINARY_STAIRCASE_UP :: GroupName TileKind
+ Content.TileKind: pattern ORIELS_FENCE :: GroupName TileKind
+ Content.TileKind: pattern POWER_SET_DARK :: GroupName TileKind
+ Content.TileKind: pattern POWER_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern PUMPS_DARK :: GroupName TileKind
+ Content.TileKind: pattern PUMPS_LIT :: GroupName TileKind
+ Content.TileKind: pattern RECT_WINDOWS :: GroupName TileKind
+ Content.TileKind: pattern ROGUE_SET :: GroupName TileKind
+ Content.TileKind: pattern RUBBLE_OR_WASTE_DARK :: GroupName TileKind
+ Content.TileKind: pattern RUBBLE_OR_WASTE_LIT :: GroupName TileKind
+ Content.TileKind: pattern SAFE_TRAIL_LIT :: GroupName TileKind
+ Content.TileKind: pattern SHOOTOUT_SET_LIT :: GroupName TileKind
+ Content.TileKind: pattern SIGNBOARD :: GroupName TileKind
+ Content.TileKind: pattern SMOKE_CLUMP_DARK :: GroupName TileKind
+ Content.TileKind: pattern SMOKE_CLUMP_LIT :: GroupName TileKind
+ Content.TileKind: pattern STAIRCASE_DOWN :: GroupName TileKind
+ Content.TileKind: pattern STAIRCASE_LIFT_DOWN :: GroupName TileKind
+ Content.TileKind: pattern STAIRCASE_LIFT_UP :: GroupName TileKind
+ Content.TileKind: pattern STAIRCASE_OUTDOOR_DOWN :: GroupName TileKind
+ Content.TileKind: pattern STAIRCASE_OUTDOOR_UP :: GroupName TileKind
+ Content.TileKind: pattern STAIRCASE_UP :: GroupName TileKind
+ Content.TileKind: pattern STAIR_TERMINAL_DARK :: GroupName TileKind
+ Content.TileKind: pattern STAIR_TERMINAL_LIT :: GroupName TileKind
+ Content.TileKind: pattern STUCK_DOOR :: GroupName TileKind
+ Content.TileKind: pattern S_BUSH_LIT :: GroupName TileKind
+ Content.TileKind: pattern S_FLOOR_ACTOR_DARK :: GroupName TileKind
+ Content.TileKind: pattern S_FLOOR_ACTOR_LIT :: GroupName TileKind
+ Content.TileKind: pattern S_FLOOR_ASHES_DARK :: GroupName TileKind
+ Content.TileKind: pattern S_FLOOR_ASHES_LIT :: GroupName TileKind
+ Content.TileKind: pattern S_FOG_LIT :: GroupName TileKind
+ Content.TileKind: pattern S_FROZEN_PATH :: GroupName TileKind
+ Content.TileKind: pattern S_HARDWARE_RACK :: GroupName TileKind
+ Content.TileKind: pattern S_LAMP_POST :: GroupName TileKind
+ Content.TileKind: pattern S_LIFT_SHAFT :: GroupName TileKind
+ Content.TileKind: pattern S_OIL_SPILL :: GroupName TileKind
+ Content.TileKind: pattern S_POOL_DARK :: GroupName TileKind
+ Content.TileKind: pattern S_POOL_LIT :: GroupName TileKind
+ Content.TileKind: pattern S_PULPIT :: GroupName TileKind
+ Content.TileKind: pattern S_REINFORCED_WALL :: GroupName TileKind
+ Content.TileKind: pattern S_SHADED_GROUND :: GroupName TileKind
+ Content.TileKind: pattern S_SHUTTLE_HULL :: GroupName TileKind
+ Content.TileKind: pattern S_SMOKE_LIT :: GroupName TileKind
+ Content.TileKind: pattern S_STAIRCASE_TRAP_DOWN_OIL :: GroupName TileKind
+ Content.TileKind: pattern S_TREE_DARK :: GroupName TileKind
+ Content.TileKind: pattern S_TREE_LIT :: GroupName TileKind
+ Content.TileKind: pattern S_UNDERBRUSH_DARK :: GroupName TileKind
+ Content.TileKind: pattern S_UNDERBRUSH_LIT :: GroupName TileKind
+ Content.TileKind: pattern TRAIL_LIT :: GroupName TileKind
+ Content.TileKind: pattern TRANSPORT_ROUTE :: GroupName TileKind
+ Content.TileKind: pattern TRAPPABLE_WALL :: GroupName TileKind
+ Content.TileKind: pattern TRAPPED_DOOR :: GroupName TileKind
+ Content.TileKind: pattern TREE_SHADE_WALKABLE_DARK :: GroupName TileKind
+ Content.TileKind: pattern TREE_SHADE_WALKABLE_LIT :: GroupName TileKind
+ Content.TileKind: pattern UNDERBRUSH_CLUMP_DARK :: GroupName TileKind
+ Content.TileKind: pattern UNDERBRUSH_CLUMP_LIT :: GroupName TileKind
+ Content.TileKind: pattern WELDED_LIFT_UP :: GroupName TileKind
+ Content.TileKind: pattern WELDED_STAIRCASE_UP :: GroupName TileKind
+ Content.TileKind: pattern ZOO_SET_DARK :: GroupName TileKind
+ Content.TileKind: pattern ZOO_SET_LIT :: GroupName TileKind

Files

Allure.cabal view
@@ -1,64 +1,62 @@ cabal-version: 2.2-name:           Allure+name:          Allure -- The package version. See the Haskell package versioning policy (PVP) -- for standards guiding when and how versions should be incremented. -- http://www.haskell.org/haskellwiki/Package_versioning_policy -- PVP summary:+-+------- breaking API changes---             | | +----- minor or non-breaking API additions---             | | | +--- code changes with no API change-version:       0.9.5.0+--             | |  +----- minor or non-breaking API additions+--             | |  | +--- code changes with no API change+version:       0.10.2.0 synopsis:      Near-future Sci-Fi roguelike and tactical squad combat game-description:   Allure of the Stars is a near-future Sci-Fi roguelike-               and tactical squad combat game. Binaries and the game manual-               are available at the homepage, where you can also-               try the game out in the browser:-               <http://allureofthestars.com/play>.-               (It runs fastest on Chrome. Keyboard commands and savefiles-               are supported only on recent enough versions of browsers.-               Mouse should work everywhere.)-               .-               Not a single picture in this game. You have to imagine everything-               yourself, like with a book (a grown-up book, without pictures).-               Once you learn to imagine things, though, you can keep exploring-               and mastering the world and making up stories for a long time.-               .-               The game is written in Haskell using the LambdaHack roguelike-               game engine <http://hackage.haskell.org/package/LambdaHack>.-               Please see the changelog file for recent improvements-               and the issue tracker for short-term plans. Long term goals-               are high replayability and auto-balancing through procedural-               content generation and persistent content modification-               based on player behaviour. Contributions are welcome.-               .-               This is a workaround .cabal file, flattened to eliminated-               internal libraries until generating haddocks for them-               is fixed. The original .cabal file is stored in the github repo.+description: Allure of the Stars is a near-future Sci-Fi roguelike+             and tactical squad combat game. Binaries and the game manual+             are available at the homepage. You can also try the game out+             in the browser at <http://allureofthestars.com/play>.+             .+             Not a single image in this game. You have to imagine everything+             yourself, like with a book (a grown-up book, without pictures).+             Once you learn to imagine things, though, you can keep exploring+             and mastering the world and making up stories for a long time.+             .+             The game is written in Haskell using the LambdaHack roguelike+             game engine <http://hackage.haskell.org/package/LambdaHack>.+             Please see the changelog file for recent improvements+             and the issue tracker for short-term plans. Long term goals+             are high replayability and auto-balancing through procedural+             content generation and persistent content modification+             based on player behaviour. Contributions are welcome.+             .+             This is a workaround .cabal file, flattened to eliminate+             internal libraries until generating haddocks for them+             is fixed. The original .cabal file is stored in the github repo. homepage:      http://allureofthestars.com bug-reports:   http://github.com/AllureOfTheStars/Allure/issues license:       AGPL-3.0-or-later license-file:  COPYLEFT-tested-with:   GHC==8.2.2, GHC==8.4.4, GHC==8.6.5-data-files:    GameDefinition/config.ui.default,-               GameDefinition/fonts/16x16xw.woff,-               GameDefinition/fonts/16x16xw.bdf,-               GameDefinition/fonts/16x16x.fnt,-               GameDefinition/fonts/8x8xb.fnt,-               GameDefinition/fonts/8x8x.fnt,-               GameDefinition/fonts/LICENSE.16x16x,-               GameDefinition/fonts/Fix15Mono-Bold.woff,-               GameDefinition/fonts/LICENSE.Fix15Mono-Bold,-               GameDefinition/InGameHelp.txt,-               GameDefinition/PLAYING.md,-               README.md,-               CHANGELOG.md,-               LICENSE,-               COPYLEFT,-               CREDITS+tested-with:   GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.4, GHC==8.10.4,+               GHC==9.0.1+-- Enough files is included to build and run CI and manual tests+-- and be presentable on Hackage, but not enough to create+-- a user-friendly binary package, e.g., no InGameHelp.txt. extra-source-files: GameDefinition/config.ui.default,-                    GameDefinition/MainMenu.ascii,-                    GameDefinition/MoveKeys.txt,                     GameDefinition/PLAYING.md,-                    Makefile+                    GameDefinition/fonts/16x16xw.woff,+                    GameDefinition/fonts/16x16xw.bdf,+                    GameDefinition/fonts/16x16x.fnt,+                    GameDefinition/fonts/8x8xb.fnt,+                    GameDefinition/fonts/8x8x.fnt,+                    GameDefinition/fonts/BinarySansProLH-Regular.ttf.woff,+                    GameDefinition/fonts/BinarySansProLH-Semibold.ttf.woff,+                    GameDefinition/fonts/BinaryCodeProLH-Bold.ttf.woff,+                    GameDefinition/fonts/DejaVuLGCSans.ttf.woff,+                    GameDefinition/fonts/DejaVuLGCSans-Bold.ttf.woff,+                    GameDefinition/fonts/Hack-Bold.ttf.woff,+                    GameDefinition/fonts/ubuntu-v14-latin-ext_latin-regular.ttf.woff,+                    GameDefinition/fonts/ubuntu-v14-latin-ext_latin-500.ttf.woff,+                    GameDefinition/fonts/ubuntu-mono-v9-latin-ext_latin-700.ttf.woff,+                    Makefile,+                    CHANGELOG.md,+                    README.md author:        Andres Loeh, Mikolaj Konarski and others maintainer:    Mikolaj Konarski <mikolaj.konarski@funktory.com> category:      Game@@ -68,6 +66,16 @@   type:               git   location:           git://github.com/AllureOfTheStars/Allure.git +flag with_expensive_assertions+  description:        turn on expensive assertions of well-tested code+  default:            False+  manual:             True++flag release+  description:        prepare for a release (expose internal functions and types, etc.)+  default:            True+  manual:             True+ flag supportNodeJS   description:        compile so that the JS blob works in terminal with NodeJS   default:            True@@ -78,22 +86,29 @@   default-extensions: MonoLocalBinds, ScopedTypeVariables, OverloadedStrings                       BangPatterns, RecordWildCards, NamedFieldPuns, MultiWayIf,                       LambdaCase, DefaultSignatures, InstanceSigs,-                      MonadFailDesugaring, StrictData, CPP+                      PatternSynonyms, StrictData, CPP   other-extensions:   TemplateHaskell, MultiParamTypeClasses, RankNTypes,                       TypeFamilies, FlexibleContexts, FlexibleInstances,                       DeriveFunctor, FunctionalDependencies,                       GeneralizedNewtypeDeriving, TupleSections,                       DeriveFoldable, DeriveTraversable,                       ExistentialQuantification, GADTs, StandaloneDeriving,-                      DataKinds, KindSignatures, DeriveGeneric+                      DataKinds, KindSignatures, DeriveGeneric, DeriveLift   ghc-options:        -Wall -Wcompat -Worphans -Wincomplete-uni-patterns -Wincomplete-record-updates -Wimplicit-prelude -Wmissing-home-modules -Widentities -Wredundant-constraints -Wmissing-export-lists -Wpartial-fields   ghc-options:        -Wall-missed-specialisations   ghc-options:        -fno-ignore-asserts -fexpose-all-unfoldings -fspecialise-aggressively -fsimpl-tick-factor=200 -  cpp-options:        -DEXPOSE_INTERNAL+  if flag(with_expensive_assertions)+    cpp-options:      -DWITH_EXPENSIVE_ASSERTIONS +  if flag(release)+    cpp-options:      -DEXPOSE_INTERNAL+   ghcjs-options:      -DUSE_JSFILE +  if !flag(supportNodeJS)+    ghcjs-options:    -DREMOVE_TELETYPE+ common exe-options   ghc-options:        -threaded -rtsopts -- (Ignored by GHCJS) Minimize median lag at the cost of occasional bigger@@ -134,17 +149,20 @@                       Implementation.MonadServerImplementation   other-modules:      Paths_Allure   autogen-modules:    Paths_Allure-  build-depends:      ,LambdaHack >= 0.9.5.0 && < 0.9.6.0+  build-depends:      ,LambdaHack >= 0.10.2.0 && < 0.10.3.0                       ,async                       ,base >= 4.10 && < 99                       ,enummapset+                      ,file-embed >= 0.0.11                       ,filepath+                      ,hsini                       ,ghc-compact                       ,optparse-applicative                       ,primitive-                      ,random-                      ,template-haskell >= 2.6+                      ,splitmix+                      ,template-haskell                       ,text+                      ,th-lift-instances                       ,transformers  executable Allure@@ -160,10 +178,17 @@ test-suite test   import: options, exe-options   type:               exitcode-stdio-1.0-  main-is:            test/test.hs+  hs-source-dirs:     test+  main-is:            Spec.hs+  other-modules:      SessionUIMock   build-depends:      ,LambdaHack                       ,Allure                       ,async                       ,base+                      ,containers                       ,filepath                       ,optparse-applicative+                      ,tasty >= 1.0+                      ,tasty-hunit+                      ,text+                      ,transformers
CHANGELOG.md view
@@ -1,5 +1,739 @@-## [v0.9.5.0](https://github.com/LambdaHack/LambdaHack/compare/v0.9.4.0...v0.9.5.0)+## [v0.10.2.0](https://github.com/AllureOfTheStars/Allure/compare/v0.9.5.0...v0.10.2.0) +- Make GLASS_HAIL less deadly and less noisy visually+- Advertise the C-Tab command instead of A-Tab+- Tweak some wearables rarity+- Make intruder's explosions less deadly+- Disable testing ubuntu fonts, because they can't be distributed in Debian+- Be more consistent about moving corrupted files aside+- Make torches a bit more common+- Help AI not to leave a scout guarding the stash+- Ensure some gems in escape scenario+- Prevent occasionally few gardening tools+- Make explosions definitions small and symmetric again+- Fix heroes starting on exit in escape scenario+- Reword some scenario texts+- Change organ symbols to avoid 'eat' when activating them+- Make gardening tools less common, but keep metal pipes+- Fix 'Letting Go' message, because it could get activated+- Hack buckler definition once more+- Hack shield definitions to help AI understand their value+- Explain in Q&A why sometimes timeout weapons are never used+- Fix backstories, because they can be manually triggered+- Replace the special periodic activation handling with effect conditions+- Fix the healing necklace better used from the backpack+- Replace the special effect handling under ranged with effect condition+- Reduce and describe the micromanagement of bucklers and shields+- Fix Letting Go not identifying even at death+- Fix typos in content texts+- Update documentation files+- Add a group for exlusively tools; unused ATM+- Fix small oil explosions never getting anywhere+- Reword initial scenario texts+- Talk about the relevant lore command in manual+- Signal that lore command is sometimes context-sensitive+- Simplify desertedAirlock description+- Reword a virtue description+- Make embeds easier to recognize knowing their tiles+- Don't end stairs with a path+- Don't describe the goal tile when performing mouse goto+- Declare the codebase lens-free+- Explain why whiskey not drunk, but smashed only+- Improve initial level descriptions+- Reword backstoryNeutral3+- Avoid distributing test.exe in Windows packages+- Fix comments about easy stairs vs hard stairs+- Fix intruders not exploding on death+- Fix Heavy Eyes preventing reaching level 4+- Remove savefiles if config file too old+- Use lz-string.js externs for minification+- Update LambdaHack.cabal.flattened+- Make focused explosions more likely to hit nearby actors+- Let one more common embed provide bad missiles+- Hlint the codebase+- Provide an excuse why the flask from fire cabinet is not identified+- Make barrels less common in raid tutorial+- Explain the flags of genetic flaw+- Avoid 'you are less more tidy'+- Help the raid scenario opponent survive a little longer+- Prevent too many torches in raid, causing blindness+- Use MinorAspects flag for backstories+- Do not include the default config file that Windows users can't read anyway+- Read config file in UTF8 mode even on Windows+- Prevent premature identification of 'Letting Go'+- Prevent premature identification of backstory items+- Update the copyright year+- Update manual wrt backstory items addition+- Add a comment about geneticFlaw not in CONDITION group+- Reorder actor and organ content+- Give the new alien a unique and glaring colour+- Shorten a backstory name to pass validation+- Make sure the backstory descriptions are actually a backstory+- Register the recently added backstory items+- Add bravery virtue+- Add alcoholist backstory item+- Add three more backstories and unify names+- Add two more vices+- Use question marks in verb effects+- Try to amass explosive monsters on arena levels+- Add an alien with an explosive organ (fixes #108)+- Add the explosive flotation bag organ+- Make sure hero professions are not randomly changing+- Hardwire the player team continuity token+- Unify and shorter backstory group names+- Flesh out initial backstory items+- Change the symbol of backstory items+- Use the flavours specialized for stories+- For now, assign heroes a backstory from a mix of all categories+- Mock up categories of backstory content items+- Add the first backstory organ+- Make sure heroes spawned in safari mode are properly numbered+- Add a few more hero names+- Mark the personal genetic flaws as meta game persistent, as they should be+- Express variety of genetic flaws as multiple content items+- Add team continuity across scenarios to content+- Make buckler and shield attacks and messages not so spammy+- Fix bucklers too powerful and shields too weak when UnderRanged activates+- Describe the new challenges in the manual+- Make shields more common and attractive to AI+- Add UnderMelee and tweak the Old Gorget+- Add a potion of melee deflection+- Buff shields with ranged deflection+- Add deflection temporary conditions+- Explain the pointman/leader discrepancy+- Write key names as on the keyboard, button names as in the button+- Unify pluralization of skills and challenges+- Mark some game modes as tutorials+- Split a list in manual in many paragraphs for better in-game display+- Keep the rest of the manual in content+- Simplify an address in manual not to spill over 80 columns+- Hint in config file to disable the special movement keys+- Hint that stealth if possible and AI vs player symmetric+- Limit mentions of 'playing' not to break immersion+- Change 'scenario' to 'adventure' to be more immersive+- Add a scenario lore menu+- Mention that scale needs to be configured for high dpi displays+- Add the fullscreen option to the UI config file+- Don't configure the unused data directories when invoking cabal+- When installing don't create the now empty GameDefinition/fonts/+- Don't limit jobs to 1 now that cabal shows errors fine+- Don't embed the fonts in the web frontend version+- Get rid of datafiles from .cabal+- Kill the font license files that are duplicated in COPYLEFT file+- Get rid of the now unused fontDir option+- Embed game-supplied font files+- Version config file+- Write the homepage address in full in Main Menu; fixes #98+- Make historyMax divisible by screenful+- Don't let ESC clear messages in aiming mode; was confusing+- Add Discord and Matrix addresses to the manual+- Add links to discord and matrix chat+- Tweak unique entities in content, adding 'the' as needed+- Add extra config options for messages+- Customize messages more comfortably+- Simplify restarting client+- Adjust weapon colours slightly towards colour meaning table+- Name the robot until somebody comes up with a better name+- Semi-retire the Allure gitter+- Reflect changes in highlighting in the manual+- Avoid unfortunate line gluing in main menu text+- Wrap messages after 53 columns+- Lower cminStairDist for shallowRogue now that margins forbidden+- Fix a missing mouse command description+- Update documentation to the config changes+- Add Debian desktop files, as per packaging policy+- Fix screensavers broken by UI faction not being the first+- Fix 'open main menu' command of Dashboard+- Comment the config file some more+- Gut out Ubuntu Family Fonts from Debian package, because non-free+- Beautify CREDITS+- Rank the fontsets differently+- Make the fontset tests more random+- Permit text usage in tests+- Settile on 'auxiliary fonts' instead of 'long text'+- Update config wrt engine changes+- Molify Debian's lintian+- Fix license name+- Legalize all the long text fonts even more+- Legalize all the long text fonts+- Add the ubuntu font family set of long text fonts+- Add another set of long text fonts+- Add new long text fonts based Adobe Source+- Remove old long text fonts+- Explain the communication overhead concept+- Fix Allure.cabal.flattened test component+- Parse config at compile time+- Let foes occupiy exit in escape scenario, etc.+- Add missing cskip specifications+- Bring back SPACE as clearning messages+- Update the default config wrt changes to RET keybinding+- Don't mention the spaceship in outdoor stairs description+- Make the brawl scenario 2-level+- Use the increased expressiveness of initial actor generation+- Make long poles a bit more common and tweak content a bit+- Don't confuse the player when trying to convey the actor is malfunctioning+- Reword ESC description again+- Add a random item to each hero starting inventory+- Tweak details of the content a bit+- Update command docs to command help changes+- Let only ESC clear messages+- Prevent AI-inaccessible areas inside shuttles+- Make spacesuits and tools more common, but later on+- Avoid 'jewelry case of gain jewelry'+- Move SPACE to CmdAim category+- Document that MMB and RMB cycle detail level+- Bind SPACE to cycle detail level in aiming mode+- Advise role-playing+- Fix calling gameover an 'endgame'+- Advise to replay initial scenarios+- Let the Welded Robot speak periodically+- Don't spoil the normal storyline in the hint about blowtorch+- Add a comment that blowtorch is safe from item polymorphing+- Add a hint about the puzzle to the manual+- Make the torn spacesuit even more common+- Name or prepare for naming all uniques+- Give all uniques their last words+- Add extra hints in the signboards on first level+- Reorder consistenly mode kind component definitions in the file+- Make hints about the first puzzle more obvious and more common+- Make torn spacesuit more common again+- Add a couple of late spawn-only actors+- Mark some actors as common among late spawns+- Factor out victoryOutcomes+- Shorten a field name to avoid >80 length lines+- Make terrain names harder to confuse with items+- Properly in-game document the screensaver scenarios+- Add missing scenario motivation blurbs+- Make the tools slightly less abundant at the start+- Tweak the scenario meta notes+- Rework game rules desccription similarly as in LH+- Add bulbousBullfrog to credits+- Add a comment to the default config with an example for rebinding a key+- Hint in-game about how stairs work+- Encourage interaction with terrain in outermost level+- Clarify that nominal values of burning and wounding are used+- Drive the point home that spacesuits differ+- Drive the point home that tools are used for crafting+- Be more explicit in the crafting tip+- Ensure the position of the boss is reported as worth visiting+- Document crafting in-game and in the manual+- Explain why octopus copes on land+- Add cabal.project.local.testing for 'make fastCrawl'+- Avoid building all exes+- Add scenario rule notes to be displayed instead of full notes+- Use hex character literals to simplify notation+- Add make target to test minified JS for node+- Make the safari easter egg less common as a screensaver+- Disable compiling from source in brew-sdl2-osx travis build, due to OOM+- Tone down and speed up animations of the deadliest explosions+- Make space for vertical dormitory rooms+- Make the dormitories more realistic given how tiny they are+- Make all ignition blasts yellow+- Hint in ambush description that somebody turned off power+- Spread evenly the loot in ambush scenario+- Make sure the initial entrance doesn't appear in ambush scenario+- Make safari scenario harder now that aliens are buffed up+- Improve module haddocks for content+- Give the player more ammo in ambush scenario+- Make zoo easier to create an ambush position in+- Make sure burning bush eventually transforms to a dark floor tile+- Make escape cave easier to put on fire+- Hide unneeded content group name patterns+- Add short-caves debug game mode+- Bump too rare places+- Use the JS splitmix optimization+- Tweak CI link in README in case it matters+- Make firefighting items more common outside of fire cabinets+- Add a Q&A about autoexplore (only relevant to Allure)+- Make sure all non-seeing actors are described so+- Move ClientOptions.hs to adhere to the convention about module hierarchies+- Meld LH and Allure content+- Don't have noctovisor neavly guaranteed+- Add items for detecting enemy stashes+- Reflavour the stuck doors trap+- Tone down spotlight+- Explain the goofy handling of recharging in stash+- Prevent exploiting deposit boxes ad infinitum+- Make detection radiuses uniform, for ease of use+- Make residential decks better connected+- Add a couple more escape items+- Adjust the SDL test to run without installing fonts, as Debian requires+- Make the jewlery caches and deposit boxes much more lucrative+- Make necklace explosions even less likely to block the way+- Make octopus more common now that it's more interesting+- Make jewlery cases less disappointing for experienced players+- Let blasts destroy ice pillars+- Fix a pair of fire projectiles first igniting, second dousing a tile+- Add Q&A about hearing+- Make military knife again likely to appear in crawl dungeon+- Let stuck doors be forced, inducing flight+- Shorten brawl scenario note to fit on the screen with square font+- Remove the confusing damage from bomb's weight impact+- Prevent exploit of getting cords from torches made out of rags+- Move most tools to first 4 levels and laboratory+- Fix crafting in water impossible when refrigeration coil equipped+- Make hearing maluses less harsh+- Make firefighting cabinets more common, particularly on outermost level+- Fix too high melee armor of a spacesuit+- Permit transformation of weld vis cold source, not only blowtorch+- Enable crafting with water sources in fire tiles+- Add some more tools and torn suits+- Boil waste container to safely remove refrigeration coil+- Differentiate the names of small fires and not the count+- Don't create the same 2 spacesuit parts when dismating a torn suit+- Make crawl loot number randomness much less wild+- Make tools more common on first two levels+- Add cord as a bonding tool and wick+- Have more tools at outermost level+- Make thornbush a bit rarer, because it doesn't exlode, so is boring+- Make arena deck of double height, with trees+- Avoid dark fences around lit interiors with no pillars in staircase defs+- Shorten other item group names+- Shorten hammer and shield group names+- Use the DECON abbreviation to shorten messages+- Fix patterns for hammers+- Mention in hammer description that crafting replaces handles+- Fit workshop crafting description into one screen+- Don't risk crafting scrap from hammers and axes+- Display cooking after crafting in embed description+- Factor out extraWaterAssocs+- Equip rags by default; newbies don't find them anyway+- Mention in octopus description that it has high noctovision+- Consistently create on the ground all items looted from terrain+- Balance removing hunger via organ hits+- Add random variety to crafted items to enable min-maxing+- Move some item definitions and rebase them+- Remove a trailing space in mode description+- Don't get two rags out of a torch that took one to produce+- Reflect keybindings change in the start scenario menu+- Increase the timeout of jumping pole+- Prevent AI from hasting twice in a row+- Add recharging and discharging self to a couple of weapons+- Get rid of DropBestWeapon+- Adjust to engine changes to charging effects+- Let the rhino charge via pushing+- Let all drones charge via pushing+- Fix a mixed up organ name+- Prevent the new organs from being dropped+- Differentiate dragon and alligator by removing the pushing from the former+- Add jumping organs+- Move ink sac to equipment so that it can be explicitly triggered+- Make octopus's ink weaker but lasting longer+- Add the ink sac octopus's organ+- Permit meleeing with construction glove+- Let burning oil explode even on direct hit on actor, not only on the floor+- Re-classify a couple of weapons+- Fix razorwire fence+- Make killing thornbush even more optional than before+- Move to-dam bonuses from organs to actor definitions+- Increase timeouts of animal organs to at least approach human weapon timeouts+- Prevent sharp harpoon from being generated often and shallowly+- Make armadillo into a proper tank+- Touch up aliens actors+- Prevent razor from excluding other weapons+- Move the defition of lip to the proper place+- Give tentancle a cooldown+- Make clawing horror an early but mostly harmless armorless tank+- Reorder organs+- Balance organs+- Add two organs from LambdaHack+- Remove cooldown from two weapon kinds+- Sometimes generate tools in threes+- Generate torn spacesuits in bunches+- Generate less items, particularly at shallow levels+- Reorder items inside several item groups+- Overhaul noise caves+- Let some tools appear together+- Designate some scouts for slack tactics factions+- Add comments explaining why dL dice positive or negative+- Adjust to crafint depth-neutral and tweak content a bit otherwise+- Make the max HP maluses not so easy to ignore+- Rebalance armor bonuses+- Rebalance to-hit bonuses+- Fix hammer stats and tweak a few others+- Make Smithhammer interesting+- Make it easier to use permanent tools+- Make it possible to undo weapon crafting+- Make axe worse than pipe+- Make item-modification scrolls and torn spacesuits more common+- Make cooking fruits less tedius, remaining defs+- Place much more oil spills on the exit deck+- Make cooking fruits less tedius+- Let the player see if enemies carry even special lights, etc.+- Don't suggest that opening a deposit box destroys it+- Improve name of a couple of item groups+- Tweak frequency of detection items, etc.+- Don't let the alien boss waste time unequipping gems+- Sometimes add a lift staircase to laboratories+- Make more rooms dark to help with stealth+- Make Laboratory deck less annoying by decontaminating only upwards+- Fix weapon order of Smithhammer+- Make it more common for aliens to carry light+- Make using and crafting harpoons more worthwhile+- Add workshop to a couple more places+- Make it more likely there is workshop in laboratory+- Tone down poison+- Make the outermost deck a little more hectic+- Hint that shields require empty space behind enemies to push them+- Make buckler a weapon+- Make the fancy shield weaker but more common+- Give a fair warning about the rhino boss+- Reorder crafting recipes to make them yet a tad safer+- Avoid lit or filled rooms (except barrels) in exit level+- Balance arena cave and scroll of pushing+- Move keybinding content definitions to match display order+- Make signboards less common in arena cave and more in casino+- Ensure small caves have one or few stairs to prevent bloating them+- Adjust cave content specifications to the engine tweaks+- Make empty flasks less common deeper down+- Clean up comments in tile definitions+- Try to make the terrain transformation specs more readable+- Mark steel file with red, since it's double-purpose+- Make blankets more common to help with light crafting+- Start with some empty flasks, some of the time+- Update the manual and keybindings printout+- Explain why the farm is freshly abandoned+- Reduce numbers of consumables now that some levels have more of them+- Add extra items to crawl levels based on their name/description+- Add ANY_FLASK group+- Introduce vials via the brawl scenario+- Customize the raid cave shape and places+- Make sure tools are not needed and stimpack not obtainable in raid scenario+- Introduce chips usage in the raid scenario+- Fix a bushy patch that can block starting actors+- Remove misleading comment about tiles being explorable+- Comment why a tile is not explorable+- Make impressing items easier to get+- Fix missing terrain in TREE_SHADE_WALKABLE_DARK+- Have lit bushes and dark floor in some dark places+- Permit auto-generated tiles to be lit in dark groups+- Comment tiles that are lit, dark or both+- Add greenery to shallow levels and more rooms+- Sometimes leave smoke after barrel explodes+- Prevent the visual senser being misinterpreted as food+- Prevent AI from wielding staves+- Make gardening tools slighly more durable, but rarer+- Set the main groups of items to equal weights and compensate+- Let all actors chase enemies from a longer distance+- Ensure few garden weapons in raid scenario and similar+- Tweak assignment of items to the rare item groups+- Rename or eliminate CURIOUS_ITEM+- Generate less survival items in short scenarios+- Add hints to crafting components' descriptions+- Tweak weapons slightly, mostly ranged damage+- Clean up throwing stats of items+- Make edible bushes possible in one more room kind+- Make permanent weapons less common initially due to clubs and torches+- Add craftable clubs+- Make torch a destructible melee weapon+- Make torch and lamp even less common now that they are crafted+- Craft flasks of water, etc.+- Fix crafting in oil and fire+- Add missing content group definitions+- Craft torches and oil lamps+- Make harpoons better in melee than ranged+- Rebalance weak weapons+- Slightly simplify organ definitions+- Add variety to armor deducting rings+- Make boots auto-equip less disastrous for newbies+- Make the echolocator ping periodically+- Change the order of ordinary items for better display in menus+- Reassign colours and symbols to embedded items+- Make some actors deal low damage but with high hurt+- Add negative armor to one boss and some conditions+- Let some rings weaken armor+- Tone down items that grant armor+- Mark in dashboard where to find crafting recipes+- Make it harder to obtain long poles from loot+- Let the net drop more kinds of armor-like equipment+- Fix broken content validation+- Don't confusingly refuse to display the pushing effect for shields+- Balance piercing missiles+- Make Rhino less of a pushover+- Update manual wrt damage calculation changes+- Avoid AI-unreachable spaces on the outermost level+- Make it cheaper to dissect a torn spacesuit, for early game+- Comment that transformations won't generate warnings due to their position+- Make tools even more common+- Undo partially scaling down special terrain features at outermost level+- Craft torch in fire, not workshop+- Tweak terrain and items on the outermost level+- Make the order of caves slightly more random+- Make edible plants less common since they appear on bushes now+- Make the starting enemy groups larger to favour throwing flasks+- Generate flasks in bunches even at lowest levels to make throwing more fun+- Make sure throwing flasks, etc., have limited radius, for safety+- Make icy floor less annoying and escape puzzles harder to hack+- Avoid generating 2-stacks of early projectiles+- Don't create equippable items in a stash+- Make the crafted and doubly crafted weapons rarer+- Fix lvl 2 description showing at game start instead of lvl 3+- Prevent heroe's foes in crawl from camping stairs on lvl 2+- Dedicate flasks to throwing and vials to medicine+- Add fixed enemies at the start of crawl, but don't spawn often+- Improve wording of a cave description+- Reduce randomness of effects to make them exploitable+- Prevent inability of taking off items that drain calm+- Update wrt macro key changes in the engine+- Disable brew audit that fails because line is too long+- Vary the number of gardening tools in the bunches+- Make craftable weapons less common+- (Almost) guarantee a fire axe, but (almost) only one+- Spawn friendlies and foes in deposit boxes+- Make the airlock supplies more random+- Create gardening tools in varied pairs+- Convey more of the shuttle story via unique embedded item, not other content+- Fix wrong game mode started due to only the first word inspected+- Prevent meleeing electric fence forever+- Port tests to tasty+- Make weapons used for crafting less common later on+- Prevent 'you are more foo' and immediately 'you are no longer foo'+- Don't let faucents slow and flash themselves+- Rename S_NITROGEN_MIST and S_FOCUSED_FLASH+- Have less particles in large oil explosions+- Make blindness less common, because it pervents player from seeing fun stuff+- Let Welded Robot establish a stash to make sure heroes be the blowtorch+- Use local gifsicle, with the assertion failure fixed+- Ensure unidentified hammers are equipped+- Tone down ranged damage of some durable items+- Replace permanent weapons with crafting even at low levels+- Make needles a bit less deadly+- Fix some Burn and RefillHP weapons becoming overpowered at range+- Simplify sharpeningAssocs+- Fix inaccessible fenceless dormitories+- Help AI replace the armor that decreases melee skill+- make cleaver weaker but more common+- Make billiard balls more common initially, less so later on+- Permit melee with gloves and helm in addition to boots+- Refactor and tweak garden weapons+- Fix gardening tools not fracturing+- Fix mixed up resistance conditions+- Describe how colours correspond to tile properties+- Prevent curing of genetic flaw via any condition removal+- Recolour transparent walls; cyan means inert, apparently+- Recolour barrels; not burning; no collision with trees+- Make the power node place more varied+- Fix AI not eating to remove hunger+- Recolour armors+- Ensure enough weak weapons at the start+- Add dormitories to the residential area cave definition+- Don't spam decent weapons at low leves now that weak weapons abound+- Make projectiles more common so that shooter aliens are not overpowered+- Craft torches out of staves+- Add a rag tangle and obtain it from torn spacesuits+- Give cleanerRobot excessive armor to make hammers useful+- Clarify that chosen weapon needs not be optimal for a foe+- Make hammers much different from all other weapons+- Make a particular barrel yard place as common as others, not much more+- Fix some terrain not alterable with projectiles+- Mention that stats menu summarizes the organ menu+- Add sample cabal.project.local files+- Make the secret walls less boring+- Use VerbMsgFail effect to ensure failed crafting doesn't change terrain+- Don't report that an animal fondles its own meat+- Add alterable floor to more rooms+- Add alterable floor to more caves and trails+- Rework frequency of immobile robots+- Desynchronise vents a bit more+- On outermost level let some faucets ignite fuel spread by other faucets+- Make hammers a bit more common+- Make frozen trail less common now that geysers produce floor ice+- Make burning oil clear, as the symbol indicates+- Adjust small fire description to match also burning oil+- Adjust crafting embedded items to the new OnCombine semantics+- Tweak timeouts of mostly harmless large sticks+- Fix wrong use of AndEffect+- Update crafting recipes to used tools and destroyed components+- Safeguard some alien ammo and treasure against stash ransacking+- Re-add perfumes; AI heroes were being slaughtered without them+- Add a torn spacesuit for crafting+- Make electric fences a less lucrative source of weapon upgrades+- Make it more likely that armor and some other items appear before lvl 12+- Generate more items now that a lot of crafting components appeared in the game+- Make barrel stacks more varied and more frequent+- Make barrel stack places more common+- Recolour faucets to match the colour of terrain they create+- Disable pathfinding through barrels and burning rubble+- Give the same on-screen colour to items with similar function+- Make the newly introduced items more common+- Make the workshop terrain more common+- Ensure item definitions are valid by having enough slots+- Don't generate ready spacesuits and avoid hard to see BrBlack items+- Make most weapons less common now that they can be crafted+- Harvest long poles from dead electric fences+- Add two useless tools to harvest long poles from+- Require a tool to but the pipe diagonally before sharpening+- Avoid the exploit of turning staffs into pipes via crafting+- Improve some weapon descriptions and comments+- Make each water a sharpening workshop to avoid scrolling recipes+- Correct some item group patterns to be singletons+- Clean up and redefine patterns from ItemKindEmbed.hs+- Let hammers be sharpened and elongated+- Require identification for all hammers+- Comment why axes and hammers are not tools+- Avoid the no-brainer of durable tool use+- Rename POLE and LONG_POLE+- Add fire axe and craft poll axe from it+- Craft long polearms and flesh out related descriptions+- Don't suggest that crowbar and cat's paw can be sharpened or blunted+- Doubly sharpen pole cleavers to ox tongues+- Doubly sharpen cleavers to get daggers+- Rename the knife item definition to avoid confusion with the new dagger+- Reorder Allure item definitions+- Sharpen halberds+- Sharpen rapiers+- Sharpen shields+- Get pipes from garden tools and sharpen them+- Enable sharpening harpoons+- Add sharpening tools+- Resking honing steel to barebones exoskeleton+- Foretell future levels accessible with spacesuits+- Add some tools+- Rename BONDING_SOURCE to BONDING_TOOL+- Fix, partially, pluralization of spacesuit trousers group+- Update to the unified tile transformation syntax+- Fix redundant Equipable or Meleeable+- Switch to the simplied ConsumeItems definition+- Eliminate the exploit of throwing durable tools at the workshop+- Define the spacestuit+- Assemble full spacesuit from the set of all its parts+- Add a spacesuit boot+- Make Autozoom Contact Lens appear in a larger percentage of games+- Let workshop work from an adjacent tile as well+- Use the new parameter of CreateItem to dilute perfume and create rose water+- Use the new parameter of CreateItem in workshop crafting engine+- Add the first workshop item crafting recipe+- Add a workshop terrain+- Remove an obsolete way of putting oil tile on fire+- Add Discharge effect to content+- Open doors with concussion blast+- Avoid renewing terrain embeds, to avoid slowdown with faucets+- Make vents terrain transforming+- Keep underbrush fluorescent also inside places+- Clean up non-essential talter attributes+- Create frozen and water tiles from damp floor+- Let underbrush grow randomly inside some places+- Let oil float over water, etc.+- Add explosive barrels+- Extend and move terrain transformation abilities within content+- Use less stuck doors while AI remains stubborn+- Don't create harpoons in bunches now that they are durable+- Revert "Make better projectiles out of weapons now that they break"+- Mark terrain transformations that are permitted with projectiles+- Make better projectiles out of weapons now that they break+- Make harpoon a weapon, but not auto-equipped+- Fix signboards requiring extra keystroke to activate+- Fix stuck doors often leaving AI helpless+- Add stuck doors to be opened by tools+- Fix unidentified embedded items impossible to trigger+- Describe the less obvious tile-altering item properties+- Add messages to dilluted OneOf effects+- Make the new tools and weapons twice more common+- Let strongest explosions breach stuff+- Describe the properties of the tools to help the player connect the dots+- Rename terrain patterns that denote possibly depleted resource+- Add tool items for altering terrain and improvised combat+- Make it possible to disarm jewelry traps+- Make edible plants grow on bushes+- Move extinguishers from walls to signboards to avoid uncovering all walls+- Make looting rubble worthwhile if weapons urgently needed+- Let actors trap down stairs with oil+- Wipe oil with a cloth+- Transform some tiles by pouring oil on them+- Make it harder to quench burning oil+- Separate singleton kind group names+- Rename HideAs to PresentAs for item kinds+- Switch texts to patterns for referring to content kind groups+- Make plants slightly less common and cleavers slightly more+- Make hunger twice as fast+- Permit PatternSynonyms+- Add a few sources of oil and fire+- Let oil spill burn+- Let signboards and pulpit burn+- Make the area of nitrogen mist smaller, but dense+- Let food thrown at lightly burning bushes get cooked+- Make meat a less perfect projectile+- Make the coil necklace not precious to freely use it+- Make underbrush more common now that it has no embed+- Get blakets and nitrogen out of walls+- Make nitrogen flask a separate item now that its explosions freeze water+- Quench fire with water and azbestos blanket+- Bring back Alter skill requirement for weld to help AI open it+- Make the spark necklace a source of cold+- Reverse order of weld modification items to help newbies understand+- Make weld durable to repeatedly depict sparking attempts to break the barrier+- Let ice be transformed into water and back+- Overcome weld with items instead of skill+- Simplify igniting greenery+- Update wrt addition of tiles altered by items+- Make sword-class weapons and exotic hammers more common+- Hint that the small fire is fine for cooking+- Make burning trees and rubble even less common on the outermost level+- Make it possible to cook plants+- Add cooked plants+- Update to binary AndEffect+- Give player more hints to start cooking+- Make burning bushes less common in crawl+- Move underbrushBurning to the right section+- Make it possible to also set bush and tree on fire+- In crawl get torches from burning trees more often+- Cook meat using a burning bush+- Let burning bush turn into burning underbrush+- Make it possible to set underbrush on fire and cook meat+- Explain away only pointman moving+- Bring hunger removal back in two places+- Fix hunger removed by some melee weapons+- Fix mercenaries using cutlery to fight+- Explain lack of tools, food and maps in crawl scenario+- Mention that all factions and actors are equal+- Reverse stars and underscores in HP bar in HUD+- Stress suvival in the game manual+- Let animals drop chunks of raw meat, for player consumption+- Mention clues and survival in backstory+- Fix medbot fissure not marked as benign+- Remove duplicated smell boost from walls (subtleFresco)+- Add a hidden wall reminding about the need to find food upstairs+- Pay for medbot healing with hunger+- Add pumpkin and let other plants remove hunger, too+- Add a hunger clock for humans and animals+- Mention leap frog in game manual+- Hint about leapfrog in brawl scenario note+- Make it easier to understand trying to go below the bottommost level is futile+- Handle the meta note about scenario separately+- Make sure tutorial scenarios have enough melee weapons+- Fix short scenario levels mistakenly thought to be relative to depth 15 not 10+- Update wrt door closing changes in the engine+- Reiterate win conditions in scenario descriptions+- Improve the descriptions for the reaction fire rings+- Mention in the manual that HP starts at half max+- Reformat game peculiarities list in the manual+- Overhaul the order and blurbs of game modes+- Make raid scenario squad-based+- Rework new game start menus+- Fix a corruption of backstory text+- Copy the list of distinguishing features from Allure homepage+- Restructure chronologically the game manual with verbs as section titles+- Get rid of 'x-hair'+- Mark some text files as out of date+- Add a couple more paragraph breaks in content descriptions+- Reformat backstory for display in main menu+- Remove text data files+- Replace old OFL-licensed font with new ones+- Re-indent the backstory+- Comment about why animals rarely eat food+- Rename tactics to doctrine+- Get rid of the henchman notion+- Rename leader to pointman+- Update decontaminator now that personal inventories are gone+- Mention the ! key whenever KP_* is mentioned+- Fix countless typos+- Start using cabal-plan+- Fix and improve Makefile, cabal file and CI scripts+- Improve and update game manual and help texts wrt game changes+- Countless changes in the LambdaHack engine and the induced changed in Allure++## [v0.9.5.0](https://github.com/AllureOfTheStars/Allure/compare/v0.9.4.0...v0.9.5.0)+ - Fix NumLock disabled in the browser - In screen reader frontend, highlight active menu line with the cursor - Fix commited debug value of rwriteSaveClips@@ -60,7 +794,7 @@ - Create a new 16x16 font and use it everywhere; tweak smaller fonts - Lock some levels or otherwise make ascending tricky - Add cooldown to most melee weapons, display that in HUD, adjust AI-- Add per-scenario and per-outcome end-game messages in content+- Add per-scenario and per-outcome gameover messages in content - Add duplicate and reroll item effects in preparation for crafting - Add actor and item analytics as a preparation for XP gain quests - Implement piercing projectiles that may share a tile with a big actor
COPYLEFT view
@@ -5,21 +5,53 @@  Files: * Copyright: 2008-2011 Andres Loeh-           2010-2019 Mikolaj Konarski and others (see git history)+           2010-2021 Mikolaj Konarski and others (see git history) License: AGPL-3.0-or-later -Files: GameDefinition/fonts/{*.fnt,*.bdf,16x16xw.woff}+Files: GameDefinition/fonts/*.fnt GameDefinition/fonts/*.bdf GameDefinition/fonts/16x16xw.woff Copyright: 1997-2016 Leon Marrick            1997-2016 Sheldon Simms III            1997-2016 Nick McConnell-           2016-2019 Mikolaj Konarski+           2016-2021 Mikolaj Konarski License: GPL-2.0-or-later -Files: GameDefinition/fonts/Fix15Mono-Bold.woff-Copyright: 2012-2015 The Mozilla Foundation and Telefonica S.A-           2016-2019 Mikolaj Konarski+Files: GameDefinition/fonts/Binary*.woff+Copyright 2010-2019 Adobe (http://www.adobe.com/), with Reserved Font Name 'Source'+Copyright 2021 Mikolaj Konarski License: OFL-1.1 +Files: GameDefinition/fonts/DejaVu*.woff+Copyright: Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.+ Bitstream Vera is a trademark of Bitstream, Inc.+ DejaVu changes are in public domain.+License: bitstream-vera+Comment:+ Bitstream Vera Sans Mono is Copyright Bitstream Inc. and licensed under the+ Bitstream Vera License with Reserved Font Names "Bitstream" and "Vera".+ .+ DejaVu modifications of the original Bitstream Vera Sans Mono typeface have+ been committed to the public domain.+ .+ The work in the Hack project is licensed under the MIT (Expat) License.+ .+ Copied from https://metadata.ftp-master.debian.org/changelogs//main/f/fonts-dejavu/fonts-dejavu_2.37-2_copyright++Files: GameDefinition/fonts/Hack*.woff+Copyright:+ 2003 Bitstream Inc.+ 2018 Christopher Simpkins <chris@sourcefoundry.org>+License: Expat and bitstream-vera+Comment:+ Bitstream Vera Sans Mono is Copyright Bitstream Inc. and licensed under the+ Bitstream Vera License with Reserved Font Names "Bitstream" and "Vera".+ .+ DejaVu modifications of the original Bitstream Vera Sans Mono typeface have+ been committed to the public domain.+ .+ The work in the Hack project is licensed under the MIT (Expat) License.+ .+ Copied from https://metadata.ftp-master.debian.org/changelogs//main/f/fonts-hack/fonts-hack_3.003-3_copyright+ Files: debian/* Copyright: held by the contributors mentioned in debian/changelog License: AGPL-3.0-or-later@@ -794,3 +826,61 @@  CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR  OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT  SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.++License: bitstream-vera+ Permission is hereby granted, free of charge, to any person obtaining a copy+ of the fonts accompanying this license ("Fonts") and associated documentation+ files (the "Font Software"), to reproduce and distribute the Font Software,+ including without limitation the rights to use, copy, merge, publish,+ distribute, and/or sell copies of the Font Software, and to permit persons to+ whom the Font Software is furnished to do so, subject to the following+ conditions:+ .+ The above copyright and trademark notices and this permission notice shall be+ included in all copies of one or more of the Font Software typefaces.+ .+ The Font Software may be modified, altered, or added to, and in particular the+ designs of glyphs or characters in the Fonts may be modified and additional+ glyphs or characters may be added to the Fonts, only if the fonts are renamed+ to names not containing either the words "Bitstream" or the word "Vera".+ .+ This License becomes null and void to the extent applicable to Fonts or Font+ Software that has been modified and is distributed under the "Bitstream Vera"+ names.+ .+ The Font Software may be sold as part of a larger software package but no copy+ of one or more of the Font Software typefaces may be sold by itself.+ .+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,+ TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL,+ SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO+ USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.+ .+ Except as contained in this notice, the names of Gnome, the Gnome Foundation,+ and Bitstream Inc., shall not be used in advertising or otherwise to promote+ the sale, use or other dealings in this Font Software without prior written+ authorization from the Gnome Foundation or Bitstream Inc., respectively. For+ further information, contact: fonts at gnome dot org.++License: Expat+ Permission is hereby granted, free of charge, to any person obtaining a+ copy of this software and associated documentation files (the "Software"),+ to deal in the Software without restriction, including without limitation+ the rights to use, copy, modify, merge, publish, distribute, sublicense,+ and/or sell copies of the Software, and to permit persons to whom the+ Software is furnished to do so, subject to the following conditions:+ .+ The above copyright notice and this permission notice shall be included+ in all copies or substantial portions of the Software.+ .+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+ OTHER DEALINGS IN THE SOFTWARE.
− CREDITS
@@ -1,52 +0,0 @@-All kinds of contributions to Allure of the Stars are gratefully welcome!-Some of the contributors are listed below, in chronological order.--Andres Loeh-Mikolaj Konarski-Krzysztof Pławski-Daniel Keefe-Pablo Reszczynski-Simon Michael---Fonts 16x16xw.woff, 16x16xw.bdf, 16x16x.fnt, 8x8x.fnt and 8x8xb.fnt-are are derived from fonts taken from-https://github.com/angband/angband, copyrighted by Leon Marrick,-Sheldon Simms III and Nick McConnell and released by them under-GNU GPL, version 2 or any later version (confirmed at-http://www.thangorodrim.net/development/opensource.html).-Any further modifications by authors of LambdaHack-are also released under GNU GPL version 2. The licence file is at-GameDefinition/fonts/LICENSE.16x16x in the source code tree.-Font 16x16xw.bdf is derived from 16x16x.fon by changing all but-a few glyphs, converting to BDF format, extending character set-and hacking the font file with bdftopcf and pcf2bdf to include-full bitmaps, not only non-zero portions, for otherwise SDL2-ttf-was not able to display the glyphs.-Font 16x16xw.woff was derived from 16x16xw.bdf by changing format-to TTF with bitsnpicas, faking descendent offsets to be 1 point lower-to prevent freetype from adding an extra pixel to the descendent,-tweaking with fontforge glyps 3 5 6 8 A a S s b d h to prevent antialiasing-of their vital parts when zoomed out, auto-hinting, manually simplifying-hints in some glyphs and converting to WOFF format.---Font Fix15Mono-Bold.woff is a modified version of-https://github.com/mozilla/Fira/blob/master/ttf/FiraMono-Bold.ttf-that is copyright 2012-2015, The Mozilla Foundation and Telefonica S.A.-The modified font is released under the SIL Open Font License, as seen in-GameDefinition/fonts/LICENSE.Fix15Mono-Bold in the source code tree.-Modifications were performed with font editor FontForge and are as follows:-* straighten and enlarge #, enlarge %, &, ', +, \,, -, :, ;, O, _, `-* centre a few other glyphs-* create a small 0x22c5-* shrink 0xb7 a bit-* extend all fonts by 150% and 150%-    (the extension resulted in an artifact in letter 'n',-     which was gleefully kept, and many other artifacts and distortions-     that should be fixed at some point)-* set width of space, nbsp and # glyphs to 1170-    (this is a hack to make DOM create square table cells)-* copy O to 0 (zero) and add the middle dot from the original zero-* randomly fix various errors and simplify with fontforge-* auto-generate hints with fontforge
GameDefinition/Content/CaveKind.hs view
@@ -1,12 +1,18 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. ----- | Cave properties.+-- | Definitions of of cave kinds. Every level in the game is an instantiated+-- cave kind. module Content.CaveKind-  ( content+  ( -- * Group name patterns+    pattern CAVE_ROGUE, pattern CAVE_ARENA, pattern CAVE_LABORATORY, pattern CAVE_NOISE, pattern CAVE_SHALLOW_ROGUE, pattern CAVE_OUTERMOST, pattern CAVE_RAID, pattern CAVE_BRAWL, pattern CAVE_BRAWL_ALT, pattern CAVE_SHOOTOUT, pattern CAVE_HUNT, pattern CAVE_ESCAPE, pattern CAVE_ZOO, pattern CAVE_AMBUSH, pattern CAVE_BATTLE, pattern CAVE_SAFARI_1, pattern CAVE_SAFARI_2, pattern CAVE_SAFARI_3+  , pattern CAVE_BRIDGE, pattern CAVE_RESIDENTIAL, pattern CAVE_MUSEUM, pattern CAVE_EXIT, pattern CAVE_CASINO, pattern CAVE_POWER+  , groupNamesSingleton, groupNames+  , -- * Content+    content   ) where  import Prelude ()@@ -15,83 +21,146 @@  import Data.Ratio -import Game.LambdaHack.Content.CaveKind-import Game.LambdaHack.Core.Dice+import           Content.ItemKind hiding+  (content, groupNames, groupNamesSingleton)+import           Content.ItemKindActor+import           Content.ItemKindEmbed hiding (ESCAPE)+import           Content.PlaceKind hiding+  (content, groupNames, groupNamesSingleton)+import           Content.TileKind hiding+  (content, groupNames, groupNamesSingleton)+import           Game.LambdaHack.Content.CaveKind+import qualified Game.LambdaHack.Content.ItemKind as IK+import           Game.LambdaHack.Content.TileKind+import           Game.LambdaHack.Core.Dice+import           Game.LambdaHack.Definition.Defs +-- * Group name patterns++groupNamesSingleton :: [GroupName CaveKind]+groupNamesSingleton = []++groupNames :: [GroupName CaveKind]+groupNames =+       [CAVE_ROGUE, CAVE_ARENA, CAVE_LABORATORY, CAVE_NOISE, CAVE_SHALLOW_ROGUE, CAVE_OUTERMOST, CAVE_RAID, CAVE_BRAWL, CAVE_BRAWL_ALT, CAVE_SHOOTOUT, CAVE_HUNT, CAVE_ESCAPE, CAVE_ZOO, CAVE_AMBUSH, CAVE_BATTLE, CAVE_SAFARI_1, CAVE_SAFARI_2, CAVE_SAFARI_3]+    ++ [CAVE_BRIDGE, CAVE_RESIDENTIAL, CAVE_MUSEUM, CAVE_EXIT, CAVE_CASINO, CAVE_POWER]++pattern CAVE_ROGUE, CAVE_ARENA, CAVE_LABORATORY, CAVE_NOISE, CAVE_SHALLOW_ROGUE, CAVE_OUTERMOST, CAVE_RAID, CAVE_BRAWL, CAVE_BRAWL_ALT, CAVE_SHOOTOUT, CAVE_HUNT, CAVE_ESCAPE, CAVE_ZOO, CAVE_AMBUSH, CAVE_BATTLE, CAVE_SAFARI_1, CAVE_SAFARI_2, CAVE_SAFARI_3 :: GroupName CaveKind++pattern CAVE_BRIDGE, CAVE_RESIDENTIAL, CAVE_MUSEUM, CAVE_EXIT, CAVE_CASINO, CAVE_POWER :: GroupName CaveKind++pattern CAVE_ROGUE = GroupName "caveRogue"+pattern CAVE_ARENA = GroupName "caveArena"+pattern CAVE_LABORATORY = GroupName "caveLaboratory"+pattern CAVE_NOISE = GroupName "caveNoise"+pattern CAVE_SHALLOW_ROGUE = GroupName "caveShallowRogue"+pattern CAVE_OUTERMOST = GroupName "caveOutermost"+pattern CAVE_RAID = GroupName "caveRaid"+pattern CAVE_BRAWL = GroupName "caveBrawl"+pattern CAVE_BRAWL_ALT = GroupName "caveBrawlAlt"+pattern CAVE_SHOOTOUT = GroupName "caveShootout"+pattern CAVE_HUNT = GroupName "caveHunt"+pattern CAVE_ESCAPE = GroupName "caveEscape"+pattern CAVE_ZOO = GroupName "caveZoo"+pattern CAVE_AMBUSH = GroupName "caveAmbush"+pattern CAVE_BATTLE = GroupName "caveBattle"+pattern CAVE_SAFARI_1 = GroupName "caveSafari1"+pattern CAVE_SAFARI_2 = GroupName "caveSafari2"+pattern CAVE_SAFARI_3 = GroupName "caveSafari3"++-- ** Allure-specific+pattern CAVE_BRIDGE = GroupName "caveBridge"+pattern CAVE_RESIDENTIAL = GroupName "caveResidential"+pattern CAVE_MUSEUM = GroupName "caveMuseum"+pattern CAVE_EXIT = GroupName "caveExit"+pattern CAVE_CASINO = GroupName "caveCasino"+pattern CAVE_POWER = GroupName "cavePower"++-- * Content+ content :: [CaveKind] content =-  [rogue, residential, arena, casino, museum, laboratory, noise, power, empty, exit, outermost, bridge, shallowRogue, raid, brawl, shootout, hunt, escape, zoo, ambush, battle, safari1, safari2, safari3]+  [rogue, residential, arena, casino, museum, laboratory, noise, power, empty, exit, outermost, bridge, shallowRogue, raid, brawl, brawlAlt, shootout, hunt, escape, zoo, ambush, battle, safari1, safari2, safari3] -rogue,    residential, arena, casino, museum, laboratory, noise, power, empty, exit, outermost, bridge, shallowRogue, raid, brawl, shootout, hunt, escape, zoo, ambush, battle, safari1, safari2, safari3 :: CaveKind+rogue,    residential, arena, casino, museum, laboratory, noise, power, empty, exit, outermost, bridge, shallowRogue, raid, brawl, brawlAlt, shootout, hunt, escape, zoo, ambush, battle, safari1, safari2, safari3 :: CaveKind  -- * On-ship "caves", that is, decks, most of mediocre height and size  rogue = CaveKind   { csymbol       = 'R'   , cname         = "Maintenance and storage"-  , cfreq         = [("default random", 100), ("caveRogue", 1)]+  , cfreq         = [(DEFAULT_RANDOM, 100), (CAVE_ROGUE, 1)]   , cXminSize     = 80   , cYminSize     = 42   , ccellSize     = DiceXY (2 `d` 4 + 10) (1 `d` 3 + 6)   , cminPlaceSize = DiceXY (2 `d` 2 + 4) (1 `d` 2 + 5)  -- sometimes merge vert.   , cmaxPlaceSize = DiceXY 16 40  -- often maximize vertically-  , cdarkOdds     = 1 `d` 54 + 1 `dL` 20-      -- most rooms lit, to compensate for dark corridors+  , cdarkOdds     = 1 `d` 50 + 1 `dL` 50+      -- most rooms lit, to compensate for dark corridors; at the bottom+      -- of the dungeon, half of the rooms lit   , cnightOdds    = 51  -- always night   , cauxConnects  = 1%2   , cmaxVoid      = 1%8-  , cminStairDist = 30-  , cextraStairs  = 1 `d` 2   , cdoorChance   = 3%4   , copenChance   = 1%5   , chidden       = 7   , cactorCoeff   = 50  -- the maze requires time to explore-  , cactorFreq    = [("monster", 50), ("animal", 20), ("robot", 30)]-  , citemNum      = 8 `d` 4 + 20 - 20 `dL` 1  -- deep down quality over quantity-  , citemFreq     = [ ("common item", 40), ("treasure", 60)-                    , ("curious item", 10) ]-  , cplaceFreq    = [("rogue", 1)]+  , cactorFreq    = [(MONSTER, 50), (ANIMAL, 20), (ROBOT, 30)]+  , citemNum      = 17 `d` 2 + 25 - 25 `dL` 1  -- at depth quality over quantity+  , citemFreq     = [ (IK.COMMON_ITEM, 40), (IK.CRAWL_ITEM, 20)+    -- CRAWL_ITEM items are used only in long scenarios, such as multi-level+    -- dungeon crawl; these may be powerful or a mundate item,+    -- unlike @TREASURE@ items+                    , (IK.TREASURE, 40) ]+    -- note that the groups are flattened; e.g., if an item is moved to another+    -- group included here with the same weight, the outcome wouldn't change+  , cplaceFreq    = [(ROGUE, 1)]   , cpassable     = False   , labyrinth     = False-  , cdefTile      = "rogueSet"-  , cdarkCorTile  = "floorCorridorDark"-  , clitCorTile   = "floorCorridorLit"-  , cwallTile     = "trappableWall"-  , ccornerTile   = "fillerWall"-  , cfenceTileN   = "basic outer fence"-  , cfenceTileE   = "habitat containment wall"-  , cfenceTileS   = "basic outer fence"-  , cfenceTileW   = "habitat containment wall"+  , cdefTile      = ROGUE_SET+  , cdarkCorTile  = FLOOR_CORRIDOR_DARK+  , clitCorTile   = FLOOR_CORRIDOR_LIT+  , cwallTile     = TRAPPABLE_WALL+  , ccornerTile   = FILLER_WALL+  , cfenceTileN   = S_BASIC_OUTER_FENCE+  , cfenceTileE   = HABITAT_CONTAINMENT_WALL+  , cfenceTileS   = S_BASIC_OUTER_FENCE+  , cfenceTileW   = HABITAT_CONTAINMENT_WALL   , cfenceApart   = False-  , clegendDarkTile = "legendDark"-  , clegendLitTile  = "legendLit"+  , clegendDarkTile = LEGEND_DARK+  , clegendLitTile  = LEGEND_LIT+  , cminStairDist = 30+  , cmaxStairsNum = 2 + 1 `d` 2   , cescapeFreq   = []-  , cstairFreq    = [ ("walled lift", 50), ("open lift", 50)-                    , ("tiny lift", 1) ]-  , cstairAllowed = [ ("walled staircase", 50), ("open staircase", 50)-                    , ("tiny staircase", 1) ]+  , cstairFreq    = [ (WALLED_LIFT, 50), (OPEN_LIFT, 50)+                    , (TINY_LIFT, 1) ]+  , cstairAllowed = [ (WALLED_STAIRCASE, 50), (OPEN_STAIRCASE, 50)+                    , (TINY_STAIRCASE, 1) ]+  , cskip         = []   , cdesc         = "Winding tunnels stretch into the dark. A few areas are passable but the remainder is packed with tanks and cells of raw materials and machinery."   } residential = rogue-  { cfreq         = [("default random", 50), ("caveResidential", 1)]+  { cfreq         = [(DEFAULT_RANDOM, 70), (CAVE_RESIDENTIAL, 1)]   , cname         = "Residential area"-  , cmaxPlaceSize = DiceXY 14 20  -- fewer big rooms+  , cminPlaceSize = DiceXY (2 `d` 2 + 4) (1 `d` 2 + 9)  -- merge vert.+  , cmaxPlaceSize = DiceXY 20 20  -- fewer vertically long rooms   , cdarkOdds     = 51  -- all rooms dark   , cnightOdds    = 0  -- always day-  , cauxConnects  = 1%10  -- differentiate; bare skeleton feel; long span paths-  , cmaxVoid      = 1%4-  , cextraStairs  = 1 + 1 `d` 2-  , cdefTile      = "fillerWall"-  , cstairFreq    = [ ("walled staircase", 50), ("open staircase", 50)-                    , ("tiny staircase", 1) ]-  , cstairAllowed = [ ("walled lift", 50), ("open lift", 50)-                    , ("tiny lift", 1) ]+  , cauxConnects  = 1%5  -- differentiate; bare skeleton feel; long span paths,+                         -- but don't overdo or a lot of backtracing needed+  , cplaceFreq    = [(ROGUE, 1), (RESIDENTIAL, 49)]+  , cdefTile      = FILLER_WALL+  , cmaxStairsNum = 3 + 1 `d` 2+  , cstairFreq    = [ (WALLED_STAIRCASE, 50), (OPEN_STAIRCASE, 50)+                    , (TINY_STAIRCASE, 1) ]+  , cstairAllowed = [ (WALLED_LIFT, 50), (OPEN_LIFT, 50)+                    , (TINY_LIFT, 1) ]   , cdesc         = "The area has been powered down, except for emergency corridors. Many suites are depressurized and sealed."   } arena = rogue   { csymbol       = 'A'   , cname         = "Recreational deck"-  , cfreq         = [("caveArena", 1)]+  , cfreq         = [(CAVE_ARENA, 1)]   , cXminSize     = 80   , cYminSize     = 25   , ccellSize     = DiceXY (3 `d` 3 + 17) (1 `d` 3 + 5)@@ -104,77 +173,80 @@   , cnightOdds    = 0  -- always day   , cauxConnects  = 1   , cmaxVoid      = 1%20-  , cminStairDist = 20-  , cextraStairs  = 1 `d` 2   , chidden       = 0   , cactorCoeff   = 40  -- smallish level, but easy to view and plan-  , cactorFreq    = [ ("monster", 50), ("animal", 70), ("robot", 5)-                    , ("aquatic", 10) ]-  , citemNum      = 7 `d` 4  -- few rooms-  , citemFreq     = [ ("common item", 20), ("treasure", 40), ("any scroll", 40)-                    , ("explosive", 40), ("curious item", 20) ]-  , cplaceFreq    = [("arena", 1)]+  , cactorFreq    = [ (EXPLOSIVE_MONSTER, 50), (ANIMAL, 50), (ROBOT, 5)+                    , (IK.AQUATIC, 10) ]+  , citemNum      = 16 `d` 2+  , citemFreq     = [ (IK.COMMON_ITEM, 20), (IK.CRAWL_ITEM, 20)+                    , (IK.TREASURE, 40)+                    , (GARDENING_TOOL, 100)  -- useless and boring by this point+                    , (IK.ANY_POTION, 200) ]  -- nature+  , cplaceFreq    = [(ARENA, 1)]   , cpassable     = True-  , cdefTile      = "arenaSetLit"-  , cdarkCorTile  = "trailLit"  -- let trails give off light-  , clitCorTile   = "trailLit"  -- may be rolled different than the above-  , cwallTile     = "openableWall"-  , cstairFreq    = [ ("walled lift", 20), ("closed lift", 80)-                    , ("tiny lift", 1) ]-  , cstairAllowed = [ ("walled staircase", 20), ("closed staircase", 80)-                    , ("tiny staircase", 1) ]-  , cdesc         = "Debris litters the wide streets and all the stalls have their shutters down."+  , cdefTile      = ARENA_SET_LIT+  , cdarkCorTile  = TRAIL_LIT  -- let trails give off light+  , clitCorTile   = TRAIL_LIT  -- may be rolled different than the above+  , cwallTile     = OPENABLE_WALL+  , cminStairDist = 20+  , cmaxStairsNum = 1 `d` 2+  , cstairFreq    = [ (WALLED_LIFT, 20), (CLOSED_LIFT, 80)+                    , (TINY_LIFT, 1) ]+  , cstairAllowed = [ (WALLED_STAIRCASE, 20), (CLOSED_STAIRCASE, 80)+                    , (TINY_STAIRCASE, 1) ]+  , cdesc         = "Debris litters the wide streets and all the stalls are either broken or have their shutters down. Nature is taking over, healing the wounds."  -- potions of healing   } casino = arena   { cname         = "Casino"-  , cfreq         = [("caveCasino", 1)]+  , cfreq         = [(CAVE_CASINO, 1)]   , cXminSize     = 21   , cYminSize     = 21   , cdarkOdds     = 41 + 1 `d` 10  -- almost all rooms lit (1 in 10 dark)   -- Trails provide enough light for fun stealth, though level too small.   , cnightOdds    = 51  -- always night-  , cminStairDist = 10   , cactorCoeff   = 70  -- cramped, don't overcrowd-  , cactorFreq    = [("monster", 50), ("animal", 25), ("robot", 50)]-  , citemNum      = 7 `d` 3  -- rare, so make it exciting, by keeping many items-  , citemFreq     = [ ("common item", 20)-                    , ("treasure", 80)  -- lives up to the name-                    , ("curious item", 40) ]-  , cdefTile      = "arenaSetDark"-  , cfenceTileN   = "habitat containment wall"  -- small cave-  , cfenceTileE   = "habitat containment wall"-  , cfenceTileS   = "habitat containment wall"-  , cfenceTileW   = "habitat containment wall"+  , cactorFreq    = [(MONSTER, 50), (ANIMAL, 25), (ROBOT, 50)]+  , citemNum      = 14 `d` 2  -- rare, so make it exciting by keeping many items+  , citemFreq     = [ (IK.COMMON_ITEM, 20)+                    , (IK.CRAWL_ITEM, 20)+                    , (IK.TREASURE, 100) ]  -- lives up to its name+  , cdefTile      = ARENA_SET_DARK+  , cfenceTileN   = HABITAT_CONTAINMENT_WALL  -- small cave+  , cfenceTileE   = HABITAT_CONTAINMENT_WALL+  , cfenceTileS   = HABITAT_CONTAINMENT_WALL+  , cfenceTileW   = HABITAT_CONTAINMENT_WALL+  , cminStairDist = 10+  , cmaxStairsNum = 2  -- to make possible 2 stairs in the last cave   , cdesc         = "The establishment is no longer filled with hollow-eyed gamblers; more dangerous things now lurk in the dark."   } museum = arena   { cname         = "Museum"-  , cfreq         = [("caveMuseum", 1)]+  , cfreq         = [(CAVE_MUSEUM, 1)]   , cXminSize     = 25   , cYminSize     = 25   , cdarkOdds     = 41 + 1 `d` 10  -- almost all rooms lit (1 in 10 dark)   -- Trails provide enough light for fun stealth, though level too small.   , cnightOdds    = 51  -- always night-  , cminStairDist = 10   , cactorCoeff   = 70  -- cramped, don't overcrowd-  , cactorFreq    = [("monster", 100), ("animal", 25), ("robot", 25)]-  , citemNum      = 7 `d` 4  -- rare, so make it exciting-  , citemFreq     = [ ("common item", 20)-                    , ("treasure", 40)-                    , ("curious item", 40)-                    , ("museum", 100) ]  -- lives up to the name-  , cplaceFreq    = [("museum", 1)]-  , cdefTile      = "museumSetDark"-  , cfenceTileN   = "habitat containment wall"  -- small cave-  , cfenceTileE   = "habitat containment wall"-  , cfenceTileS   = "habitat containment wall"-  , cfenceTileW   = "habitat containment wall"-  , cdesc         = "History has shown that museal treasures are safer in space than anywhere on Earth. Also, cruise passengers eagerly visit exhibitions, even if over the weeks of the journey they become increasingly a captive audience. That rarely applies to spaceship crew and yet museum security has a particularly keen eye for the working men visiting their establishments. Quite often a museum is the only place within millions of kilometers to house a desperately needed tool, old but sturdy beyond anything a 3D printer could produce."+  , cactorFreq    = [(MONSTER, 100), (ANIMAL, 25), (ROBOT, 25)]+  , citemNum      = 16 `d` 2  -- rare, so make it exciting+  , citemFreq     = [ (IK.COMMON_ITEM, 20)+                    , (IK.CRAWL_ITEM, 20)+                    , (IK.TREASURE, 20)+                    , (MUSEAL, 200) ]  -- lives up to its name+  , cplaceFreq    = [(MUSEUM, 1)]+  , cdefTile      = MUSEUM_SET_DARK+  , cfenceTileN   = HABITAT_CONTAINMENT_WALL  -- small cave+  , cfenceTileE   = HABITAT_CONTAINMENT_WALL+  , cfenceTileS   = HABITAT_CONTAINMENT_WALL+  , cfenceTileW   = HABITAT_CONTAINMENT_WALL+  , cminStairDist = 10+  , cdesc         = "History has shown that museal treasures are safer in space than anywhere on Earth. Passengers eagerly attend exhibitions, even if over the weeks of the journey they become increasingly a captive audience. Spaceship crew are not that enthusiastic but even they find reasons to pay visits despite misgivings of museum security. Quite often a museum is the only place within millions of kilometers to house a desperately needed tool, old but sturdy beyond anything a 3D printer can produce."   } laboratory = rogue   { csymbol       = 'L'   , cname         = "Laboratory"-  , cfreq         = [("caveLaboratory", 1)]+  , cfreq         = [(CAVE_LABORATORY, 1)]   , cXminSize     = 60   , cYminSize     = 42   , ccellSize     = DiceXY (1 `d` 2 + 5) (1 `d` 2 + 7)@@ -183,86 +255,90 @@   , cnightOdds    = 0  -- always day so that the corridor smoke is lit   , cauxConnects  = 1%5   , cmaxVoid      = 1%10-  , cminStairDist = 25-  , cextraStairs  = 1 `d` 2   , cdoorChance   = 1   , copenChance   = 1%2-  , cactorFreq    = [ ("monster", 50), ("animal", 70), ("robot", 5)-                    , ("aquatic", 10) ]-  , citemNum      = 7 `d` 5  -- reward difficulty-  , citemFreq     = [ ("common item", 20), ("treasure", 40), ("potion", 40)-                    , ("curious item", 40) ]-  , cplaceFreq    = [("laboratory", 1)]-  , cdefTile      = "fillerWall"-  , cdarkCorTile  = "labTrailLit"  -- let lab smoke give off light always-  , clitCorTile   = "labTrailLit"-  , cstairFreq    = [ ("decontaminating walled staircase", 50)-                    , ("decontaminating open staircase", 50)-                    , ("decontaminating tiny staircase", 1) ]+  , cactorFreq    = [ (MONSTER, 50), (ANIMAL, 70), (ROBOT, 5)+                    , (IK.AQUATIC, 10) ]+  , citemNum      = 20 `d` 2  -- reward difficulty, despite fewer rooms+  , citemFreq     = [ (IK.COMMON_ITEM, 20), (IK.CRAWL_ITEM, 40)+                    , (IK.TREASURE, 40), (IK.EXPLOSIVE, 80) ]+  , cplaceFreq    = [(LABORATORY, 1)]+  , cdefTile      = FILLER_WALL+  , cdarkCorTile  = LAB_TRAIL_LIT  -- let lab smoke give off light always+  , clitCorTile   = LAB_TRAIL_LIT+  , cminStairDist = 25+  , cmaxStairsNum = 1 `d` 2+  , cstairFreq    = [ (DECON_WALLED_LIFT, 50)+                    , (DECON_OPEN_LIFT, 50)+                    , (DECON_TINY_LIFT, 1) ]       -- In lone wolf challenge, the player better summoned or dominated       -- any helpers by this point. If not, good luck fighting bare-handed.-  , cstairAllowed = [ ("decontaminating walled lift", 50)-                    , ("decontaminating open lift", 50)-                    , ("decontaminating tiny lift", 1) ]-  , cdesc         = "Shattered glassware and the sharp scent of spilt chemicals show that something terrible happened here."+  , cstairAllowed = [ (DECON_WALLED_STAIRCASE, 50)+                    , (DECON_OPEN_STAIRCASE, 50)+                    , (DECON_TINY_STAIRCASE, 1) ]+  , cdesc         = "Shattered glassware and the sharp scent of spilt chemicals show that something terrible happened here. The reinforced bulkheads muffle furious roars."  -- a fair warning to skip the level or regroup   } noise = rogue   { csymbol       = 'N'   , cname         = "Computing hardware hub"-  , cfreq         = [("caveNoise", 1)]-  , cXminSize     = 21+  , cfreq         = [(CAVE_NOISE, 1)]+  , cXminSize     = 25   , cYminSize     = 42-  , ccellSize     = DiceXY (3 `d` 5 + 12) 8-  , cminPlaceSize = DiceXY 8 7  -- often merge vertically-  , cmaxPlaceSize = DiceXY 20 20+  , ccellSize     = DiceXY (1 `d` 3 + 7) 8+  , cminPlaceSize = DiceXY 7 7  -- often merge vertically+  , cmaxPlaceSize = DiceXY 8 20   , cdarkOdds     = 51   -- Light is deadly, because nowhere to hide and pillars enable spawning   -- very close to heroes.   , cnightOdds    = 0  -- harder variant, but looks cheerful   , cauxConnects  = 1%10   , cmaxVoid      = 1%100-  , cminStairDist = 15-  , cextraStairs  = 1   , cdoorChance   = 1  -- to enable the doorlessWall hack   , chidden       = 0   , cactorCoeff   = 70  -- the maze requires time to explore; also, small-  , cactorFreq    = [("monster", 100), ("animal", 5), ("robot", 25)]-  , citemNum      = 8 `d` 5  -- an incentive to explore the labyrinth-  , citemFreq     = [ ("common item", 20), ("treasure", 60), ("explosive", 20)-                    , ("curious item", 30) ]+  , cactorFreq    = [(MONSTER, 100), (ANIMAL, 5), (ROBOT, 25)]+  , citemNum      = 16 `d` 2  -- an incentive to explore the labyrinth+  , citemFreq     = [ (IK.COMMON_ITEM, 40), (IK.CRAWL_ITEM, 40)+                    , (IK.TREASURE, 40), (IK.ANY_SCROLL, 200) ]+  , cplaceFreq    = [(NOISE, 1)]   , cpassable     = True   , labyrinth     = True-  , cplaceFreq    = [("noise", 1)]-  , cdefTile      = "noiseSetLit"-  , cwallTile     = "openableWall"+  , cdefTile      = NOISE_SET_LIT+  , cwallTile     = OPENABLE_WALL   , cfenceApart   = True  -- ensures no cut-off parts from collapsed-  , cdarkCorTile  = "damp floor Dark"-  , clitCorTile   = "damp floor Lit"-  , cstairFreq    = [ ("closed staircase", 50), ("open staircase", 50)-                    , ("tiny staircase", 1) ]-  , cstairAllowed = [ ("closed lift", 50), ("open lift", 50)-                    , ("tiny lift", 1) ]-  , cdesc         = "Several machines still function, processors whirring through routines scheduled by dead men."+  , cdarkCorTile  = DAMP_FLOOR_DARK+  , clitCorTile   = DAMP_FLOOR_LIT+  , cminStairDist = 15+  , cmaxStairsNum = 1+  , cstairFreq    = [ (CLOSED_LIFT, 50), (OPEN_LIFT, 50)+                    , (TINY_LIFT, 1) ]+  , cstairAllowed = [ (CLOSED_STAIRCASE, 50), (OPEN_STAIRCASE, 50)+                    , (TINY_STAIRCASE, 1) ]+  , cdesc         = "Several machines still function, processors whirring through routines scheduled by dead men. Some scattered chips can still be read."   } power = noise   { cname         = "Power distribution hub"-  , cfreq         = [("cavePower", 1)]+  , cfreq         = [(CAVE_POWER, 1)]   , cXminSize     = 32   , cYminSize     = 42+  , ccellSize     = DiceXY (1 `d` 5 + 9) 9+  , cminPlaceSize = DiceXY 7 7+  , cmaxPlaceSize = DiceXY 20 20   , cnightOdds    = 51  -- easier variant, but looks sinister-  , cextraStairs  = 2-  , citemNum      = 8 `d` 5  -- an incentive to explore the final labyrinth-  , citemFreq     = [ ("common item", 20), ("gem", 80), ("curious item", 30) ]+  , citemNum      = 16 `d` 2+  , citemFreq     = [(IK.COMMON_ITEM, 20), (IK.CRAWL_ITEM, 10), (GEM, 80)]                       -- can't be "valuable" or template items generated-  , cdefTile      = "powerSetDark"-  , cdarkCorTile  = "oily floor Dark"-  , clitCorTile   = "oily floor Lit"-  , cstairFreq    = [ ("gated closed staircase", 50)-                    , ("gated open staircase", 50)-                    , ("gated tiny staircase", 1) ]-  , cstairAllowed = [ ("gated closed lift", 50)-                    , ("gated open lift", 50)-                    , ("gated tiny lift", 1) ]+  , cdefTile      = POWER_SET_DARK+  , cdarkCorTile  = OILY_FLOOR_DARK+  , clitCorTile   = OILY_FLOOR_LIT+  , cmaxStairsNum = 2+    -- This determines that the continuous staircase uses stairs, not lifts.+  , cstairFreq    = [ (GATED_CLOSED_STAIRCASE, 50)+                    , (GATED_OPEN_STAIRCASE, 50)+                    , (GATED_TINY_STAIRCASE, 1) ]+  , cstairAllowed = [ (GATED_CLOSED_LIFT, 50)+                    , (GATED_OPEN_LIFT, 50)+                    , (GATED_TINY_LIFT, 1) ]   , cdesc         = "A trickle of energy flows through a hub that could power a city. The air is warm and carries organic stench. Once in a while a young animal scurries across a lit patch of ground, pouncing in low gravity."   } empty = rogue@@ -276,44 +352,43 @@   , cnightOdds    = 0  -- always day   , cauxConnects  = 3%2   , cmaxVoid      = 0  -- too few rooms to have void and fog common anyway-  , cminStairDist = 40-  , cextraStairs  = 1   , cdoorChance   = 1  -- to enable the doorlessWall hack   , chidden       = 0   , cactorCoeff   = 40  -- easy to view and plan-  , cactorFreq    = [("monster", 10), ("animal", 5), ("robot", 85)]-  , citemNum      = 7 `d` 4  -- few rooms-  , cplaceFreq    = [("empty", 1)]+  , cactorFreq    = [(MONSTER, 10), (ANIMAL, 5), (ROBOT, 85)]+  , citemNum      = 16 `d` 2  -- lots of free space, but extra loot present+  , cplaceFreq    = [(EMPTY, 1)]   , cpassable     = True-  , cdefTile      = "emptySetLit"-  , cdarkCorTile  = "floorArenaDark"-  , clitCorTile   = "floorArenaLit"-  , cwallTile     = "openableWall"+  , cdefTile      = EMPTY_SET_LIT+  , cdarkCorTile  = FLOOR_ARENA_DARK+  , clitCorTile   = FLOOR_ARENA_LIT+  , cwallTile     = OPENABLE_WALL   , cfenceApart   = True  -- ensures no cut-off border airlocks and tanks-  , cstairFreq    = [ ("walled lift", 20), ("closed lift", 80)-                    , ("tiny lift", 1) ]-  , cstairAllowed = [ ("walled staircase", 20), ("closed staircase", 80)-                    , ("tiny staircase", 1) ]+  , cminStairDist = 40+  , cstairFreq    = [ (WALLED_LIFT, 20), (CLOSED_LIFT, 80)+                    , (TINY_LIFT, 1) ]+  , cstairAllowed = [ (WALLED_STAIRCASE, 20), (CLOSED_STAIRCASE, 80)+                    , (TINY_STAIRCASE, 1) ]   , cdesc         = "Not much to see here yet."   } exit = empty   { cname         = "Shuttle servicing level"-  , cfreq         = [("caveExit", 1)]+  , cfreq         = [(CAVE_EXIT, 1)]   , ccellSize     = DiceXY (1 `d` 2 + 20) 16   , cmaxPlaceSize = DiceXY 25 20   , cdarkOdds     = 51  -- all dark to compensate for the always lit shuttles-  , cextraStairs  = 2-  , cplaceFreq    = [("exit", 1)]-  , cdefTile      = "exitSetLit"-  , cdarkCorTile  = "transport route"-  , clitCorTile   = "transport route"-  , cfenceTileN   = "basic outer fence"-  , cfenceTileE   = "habitat containment wall"-  , cfenceTileS   = "airlock fence"-  , cfenceTileW   = "habitat containment wall"-  , cescapeFreq   = [("escape spaceship down", 1)]-  , cstairFreq    = [("walled lift", 20), ("tiny lift", 1)]-  , cstairAllowed = [("walled staircase", 20), ("tiny staircase", 1)]+  , cplaceFreq    = [(EXIT, 1)]+  , cdefTile      = EXIT_SET_LIT+  , cdarkCorTile  = TRANSPORT_ROUTE+  , clitCorTile   = TRANSPORT_ROUTE+  , cfenceTileN   = S_BASIC_OUTER_FENCE+  , cfenceTileE   = HABITAT_CONTAINMENT_WALL+  , cfenceTileS   = AIRLOCK_FENCE+  , cfenceTileW   = HABITAT_CONTAINMENT_WALL+  , cmaxStairsNum = 2  -- too many hinder generation of shuttles+  , cescapeFreq   = [(ESCAPE_FROM_SPACESHIP_DOWN, 1)]+  , cstairFreq    = [(WALLED_LIFT, 20), (TINY_LIFT, 1)]+  , cstairAllowed = [(WALLED_STAIRCASE, 20), (TINY_STAIRCASE, 1)]   , cdesc         = "Empty husks and strewn entrails of small craft litter the hangar among cranes and welding machines. The distant main fusion thruster array can be seen to the rear of the spaceship through oriels and airlocks of all sizes."       -- E and W sides are borders with other level sections, so no oriels.       -- The meteor shield towards N is not punctured here, because@@ -321,94 +396,106 @@   } outermost = empty   { cname         = "Outermost deck"-  , cfreq         = [("caveOutermost", 100)]-  , cactorCoeff   = 5  -- shallower than LH, so fewer immediate actors, so boost-  , cactorFreq    = [ ("animal", 3), ("robot", 1)-                    , ("immobile robot", 90), ("immobile animal", 2)-                    , ("aquatic animal", 2) ]  -- ("aquatic robot", 2)+  , cfreq         = [(CAVE_OUTERMOST, 100)]+  , cactorCoeff   = 4  -- shallower than LH, so fewer immediate actors, so boost+  , cactorFreq    = [ (ANIMAL, 3), (ROBOT, 1)+                    , (IMMOBILE_ROBOT, 90), (IMMOBILE_ANIMAL, 2)+                    , (AQUATIC_ANIMAL, 2) ]  -- (AQUATIC_ROBOT, 2)       -- The medbot faucets on lvl 1 act like HP resets. Needed to avoid       -- cascading failure, if the particular starting conditions were       -- very hard. Items are not reset, even if they are bad, which provides       -- enough of a continuity. The faucets on lvl 1 are not OP and can't be       -- abused, because they spawn less and less often and also HP doesn't       -- effectively accumulate over max.-  , citemFreq     = ("starting weapon", 20)-                    : filter ((/= "treasure") . fst) (citemFreq empty)-  , cfenceTileN   = "oriels fence"-  , cfenceTileE   = "habitat containment wall"-  , cfenceTileS   = "empty airlock fence"-  , cfenceTileW   = "habitat containment wall"-  , cdesc         = "The black sky outside sucks light through the oriel and airlock glass in the walls and floor of this outermost level. Each minute, the dusky melancholic light of the distant Sun attempts for a few seconds to squeeze in but is repelled by artificial lighting. The mucky floor looks misleadingly straight, its curvature noticeable only across the whole extent of the hull section. Overflowing water treatment basins and series of hanging and stacked tanks double as radiation shields. Hoses writhe on the ground and dangle in thick knots from the ceiling. This deck is the main pressurized cargo bay and storage, with the only other docking hub for small craft somewhere among the giant spaceship's upper levels. You recognize an inner airlock door smeared with guano, but you can't see the shuttle you left engaged to the airlock clamps outside. "+  , citemFreq     = [ (IK.COMMON_ITEM, 50), (IK.CRAWL_ITEM, 50)+                    , (GARDENING_TOOL, 600) ]+  , cfenceTileN   = ORIELS_FENCE+  , cfenceTileE   = HABITAT_CONTAINMENT_WALL+  , cfenceTileS   = EMPTY_AIRLOCK_FENCE+  , cfenceTileW   = HABITAT_CONTAINMENT_WALL+  , cmaxStairsNum = 2+  , cdesc         = "This is as far as one can go \"down\". The void outside sucks light through the oriel and airlock glass in the walls and floor of this outermost level. Each minute, the dusky melancholic light of the distant Sun attempts for a few seconds to squeeze in but is repelled by artificial lighting.\nThe mucky floor marked by unkempt greenery looks misleadingly straight, its curvature noticeable only across the whole extent of the hull section. Overflowing water basins and series of hanging and stacked tanks double as radiation shields. Hoses writhe on the ground and dangle in thick knots from the ceiling. With proper tools, some of the containers could be opened and working spaces productively employed. There is no junk is space.\nThis deck is the main pressurized cargo bay and storage, with the only other docking hub for small craft located among the giant spaceship's upper levels. Somewhere here must be the airlock you docked your shuttle to and stacked your supplies against."       -- E and W sides are borders with other level sections, so no oriels.   } bridge = rogue   { csymbol       = 'B'   , cname         = "Captain's bridge"-  , cfreq         = [("caveBridge", 1)]+  , cfreq         = [(CAVE_BRIDGE, 1)]   , cXminSize     = 30   , cYminSize     = 30   , ccellSize     = DiceXY (2 `d` 4 + 5) (1 `d` 2 + 5)   , cminPlaceSize = DiceXY (2 `d` 2 + 3) (1 `d` 2 + 4)  -- sometimes merge all   , cmaxPlaceSize = DiceXY 16 20-  , cminStairDist = 10   , cdarkOdds     = 0  -- all rooms lit, for a gentle start   , cauxConnects  = 2  -- few rooms, so many corridors-  , cextraStairs  = 1   , cactorCoeff   = 300  -- it's quite deep already, so spawn slowly;-                         -- initially best for sleeping, then all catch up-  , cactorFreq    = [("animal", 100)]-  , citemNum      = 8 `d` 3  -- lure them in with loot-  , citemFreq     = filter ((`notElem` ["treasure", "curious item"]) . fst)-                    $ citemFreq rogue-  , cdefTile      = "fillerWall"-  , cfenceTileN   = "habitat containment wall"  -- cave isolated for safety-  , cfenceTileE   = "habitat containment wall"-  , cfenceTileS   = "habitat containment wall"-  , cfenceTileW   = "habitat containment wall"-  , cstairFreq    = [ ("welded walled lift", 50)-                    , ("welded open lift", 50)-                    , ("welded tiny lift", 1) ]-  , cstairAllowed = [ ("welded walled staircase", 50)-                    , ("welded open staircase", 50)-                    , ("welded tiny staircase", 1) ]-  , cdesc         = "The bridge is gutted out and nonoperational. There are animal cries down below and ominous silence up above."+                         -- this is initially the best level for sleeping+  , cactorFreq    = [(ANIMAL, 100)]+  , citemNum      = 12 `d` 2  -- lure them in with loot+  , citemFreq     = [(IK.COMMON_ITEM, 100), (GARDENING_TOOL, 800)]+  , cdefTile      = FILLER_WALL+  , cfenceTileN   = HABITAT_CONTAINMENT_WALL  -- cave isolated for safety+  , cfenceTileE   = HABITAT_CONTAINMENT_WALL+  , cfenceTileS   = HABITAT_CONTAINMENT_WALL+  , cfenceTileW   = HABITAT_CONTAINMENT_WALL+  , cminStairDist = 10+  , cmaxStairsNum = 1+  , cstairFreq    = [ (WELDED_WALLED_LIFT, 50)+                    , (WELDED_OPEN_LIFT, 50)+                    , (WELDED_TINY_LIFT, 1) ]+  , cstairAllowed = [ (WELDED_WALLED_STAIRCASE, 50)+                    , (WELDED_OPEN_STAIRCASE, 50)+                    , (WELDED_TINY_STAIRCASE, 1) ]+  , cdesc         = "The bridge is gutted out and nonoperational. You saved space on the shuttle by only packing demolition equipment (and booze, long gone, all flasks flung to fend off the annoying vermin) and now you can't even attempt repairs. You are also short on rations and vials of medicine to treat your recent wounds. Only water is plentiful on the ship: gaseous, liquid, frozen. There are animal cries down below and ominous silence up above."   } shallowRogue = rogue-  { cfreq         = [("caveShallowRogue", 100)]+  { cfreq         = [(CAVE_SHALLOW_ROGUE, 100)]   , cXminSize     = 60   , cYminSize     = 37-  , cextraStairs  = 2-  , cactorCoeff   = 40  -- more difficult-  , cactorFreq    = filter ((/= "monster") . fst) $ cactorFreq rogue-  , citemNum      = 8 `d` 4-  , citemFreq     = ("starting weapon", 20)-                    : filter ((/= "treasure") . fst) (citemFreq rogue)-  , cdesc         = "This close to the outermost deck, residence is not permitted and walls and doors are sturdier, to contain a theoretically possible micro-meteorite breach. The entry is not closed off, though, because some passengers can't live without a regular pilgrimage to 'look outside'. Apparently, gazing at the sharp pin-points of stars and planets through the reinforced oriel glass is incomparable to watching the same through the thin polymer of wall displays. Animals appear to share the fascination of outer decks, perhaps attracted by the increased gravity, nearly Earth-like, unlike elsewhere on the ship."+  , cactorCoeff   = 120  -- more difficult+  , cactorFreq    = filter ((/= MONSTER) . fst) $ cactorFreq rogue+  , citemNum      = 14 `d` 2+  , citemFreq     = [ (IK.COMMON_ITEM, 40), (IK.CRAWL_ITEM, 60)+                    , (GARDENING_TOOL, 700), (IK.ANY_FLASK, 200) ]+  , cminStairDist = 15+  , cmaxStairsNum = 2+  , cskip         = [0, 1]  -- ban foes camping on either stairs+  , cdesc         = "This close to the outermost deck, residence is not permitted and walls and doors are sturdier to contain a theoretically possible micro-meteorite breach. The entry is not closed off, though, because some passengers can't live without a regular pilgrimage to 'look outside' and the only way to the bottom-most level leads through here. Apparently, gazing at the sharp pin-points of stars and planets through the reinforced oriel glass is incomparable to watching the same through the thin polymer of wall displays.\nAnimals appear to share the fascination of outer decks, perhaps attracted by the increased gravity, nearly Earth-like, unlike elsewhere on the ship. However, they dislike many industrial fluids stored on these floors, so flinging random flasks at them works as an effective deterrent. Moreover, if you throw an unidentified flask, you can be sure you won't waste a badly needed nano medicine, because it's never stored in such large containers. Even tiny vials cost a fortune."   }  -- * "Caves" on various celestial bodies (including, but not limited to, moons,---   with virtually no story-wise limits wrt height and size+--    with virtually no story-wise limits wrt height and size.+--    Here @citemNum@ is very random, for interesting replays; no risk+--    of bad rolls accumulating and trashing a long game, particularly+--    in symmetric scenarios, where both parties can use extra loot+--    equally well.  raid = rogue   { csymbol       = 'S'   , cname         = "Triton City sewers"-  , cfreq         = [("caveRaid", 1)]-  , cXminSize     = 50+  , cfreq         = [(CAVE_RAID, 1)]+  , cXminSize     = 60  -- long sewer tunnels   , cYminSize     = 21   , ccellSize     = DiceXY (2 `d` 2 + 7) 6   , cminPlaceSize = DiceXY (2 `d` 2 + 4) 5  -- sometimes merge all   , cmaxPlaceSize = DiceXY 16 20   , cdarkOdds     = 0  -- all rooms lit, for a gentle start   , cmaxVoid      = 1%10-  , cdefTile      = "fillerWall"-  , cextraStairs  = 0+  , cdefTile      = FILLER_WALL   , cactorCoeff   = 250  -- deep level with no kit, so slow spawning-  , cactorFreq    = [("animal", 50), ("robot", 50)]-  , citemNum      = 6 `d` 6  -- just one level, hard enemies, treasure-  , citemFreq     = [("common item", 100), ("currency", 500)]-  , cescapeFreq   = [("escape up", 1)]+  , cactorFreq    = [(ANIMAL, 50), (ROBOT, 50)]+  , citemNum      = 9 `d` 6  -- just one level, hard enemies, treasure+  , citemFreq     = [ (IK.COMMON_ITEM, 30)+                    , (STARTING_ARMOR, 100), (STARTING_WEAPON, 300)+                    , (WEAK_ARROW, 100), (LIGHT_ATTENUATOR, 50)+                    , (IK.S_CURRENCY, 400), (IK.ANY_SCROLL, 100) ]+                    -- introducing chips in this scenario+  , cplaceFreq    = [(RAID, 1)]+  , cmaxStairsNum = 0+  , cescapeFreq   = [(INDOOR_ESCAPE_UP, 1)]   , cstairFreq    = []   , cstairAllowed = []+  , cskip         = [0]   , cdesc         = "Mold spreads across the walls and scuttling sounds can be heard in the distance."   } brawl = rogue  -- many random solid tiles, to break LOS, since it's a day@@ -416,7 +503,7 @@                -- also, sanctuaries against missiles in shadow under trees   { csymbol       = 'S'   , cname         = "Woodland biosphere"-  , cfreq         = [("caveBrawl", 1)]+  , cfreq         = [(CAVE_BRAWL, 1)]   , cXminSize     = 60   , cYminSize     = 30   , ccellSize     = DiceXY (2 `d` 5 + 5) 7@@ -426,21 +513,34 @@   , cnightOdds    = 0   , cdoorChance   = 1   , copenChance   = 0-  , cextraStairs  = 0   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 5 `d` 6-  , citemFreq     = [("common item", 100)]-  , cplaceFreq    = [("brawl", 1)]+  , citemNum      = 6 `d` 6+  , citemFreq     = [ (IK.COMMON_ITEM, 50)+                    , (STARTING_WEAPON, 200), (STARTING_ARMOR, 400)+                    , (IK.ANY_SCROLL, 100), (IK.ANY_POTION, 600) ]+                    -- introducing vials in this scenario+  , cplaceFreq    = [(BRAWL, 1)]   , cpassable     = True-  , cdefTile      = "brawlSetLit"-  , cdarkCorTile  = "dirt Lit"-  , clitCorTile   = "dirt Lit"-  , cwallTile     = "openableWall"-  , cstairFreq    = []+  , cdefTile      = BRAWL_SET_LIT+  , cdarkCorTile  = DIRT_LIT+  , clitCorTile   = DIRT_LIT+  , cwallTile     = OPENABLE_WALL+  , cmaxStairsNum = 1+  , cstairFreq    = [(OUTDOOR_TINY_STAIRCASE, 1)]   , cstairAllowed = []+  , cskip         = []  -- start heroes on stairs, since they are created first   , cdesc         = "Shadows pool under the trees and leaves crunch underfoot."   }+brawlAlt = brawl+  { cfreq         = [(CAVE_BRAWL_ALT, 1)]+  , cmaxVoid      = 1%100  -- to ensure enough trees+  , cdefTile      = HUNT_SET_LIT  -- alt+  , cdarkCorTile  = S_FROZEN_PATH  -- alt+  , clitCorTile   = S_FROZEN_PATH+  , cskip         = [0]  -- ban foes camping on stairs+  , cdesc         = "With the Sun so distant, biospheres are stacked vertically rather than horizontally, sharing drainage, not lighting."+  } shootout = rogue  -- a scenario with strong missiles;                   -- few solid tiles, but only translucent tiles or walkable                   -- opaque tiles, to make scouting and sniping more interesting@@ -448,40 +548,41 @@                   -- scenario is about ranged combat at long range   { csymbol       = 'S'   , cname         = "Hydroponic farm"  -- still a neutral, official wording-  , cfreq         = [("caveShootout", 1)]+  , cfreq         = [(CAVE_SHOOTOUT, 1)]   , ccellSize     = DiceXY (1 `d` 2 + 5) 6   , cminPlaceSize = DiceXY 3 3  -- rarely merge vertically   , cmaxPlaceSize = DiceXY 5 5-  , cdarkOdds     = 51+  , cdarkOdds     = 0  -- all lit, not to duplicate the @hunt@ ranged tactics   , cnightOdds    = 0   , cauxConnects  = 1%10   , cdoorChance   = 1   , copenChance   = 0-  , cextraStairs  = 0   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 5 `d` 16+  , citemNum      = 6 `d` 16                       -- less items in inventory, more to be picked up,                       -- to reward explorer and aggressor and punish camper-  , citemFreq     = [ ("common item", 30)-                    , ("any arrow", 400), ("harpoon", 300), ("explosive", 50) ]+  , citemFreq     = [ (IK.COMMON_ITEM, 30), (GARDENING_TOOL, 500)+                    , (ANY_ARROW, 400), (HARPOON, 200), (IK.EXPLOSIVE, 300) ]                       -- Many consumable buffs are needed in symmetric maps                       -- so that aggressor prepares them in advance and camper                       -- needs to waste initial turns to buff for the defence.-  , cplaceFreq    = [("shootout", 1)]+  , cplaceFreq    = [(SHOOTOUT, 1)]   , cpassable     = True-  , cdefTile      = "shootoutSetLit"-  , cdarkCorTile  = "dirt Lit"-  , clitCorTile   = "dirt Lit"-  , cwallTile     = "openableWall"+  , cdefTile      = SHOOTOUT_SET_LIT+  , cdarkCorTile  = DIRT_LIT+  , clitCorTile   = DIRT_LIT+  , cwallTile     = OPENABLE_WALL+  , cmaxStairsNum = 0   , cstairFreq    = []   , cstairAllowed = []-  , cdesc         = "Once so carefully curated, the planting beds are now overgrown and choked with weeds."  -- when we know what fits the plot, suggest it here; e.g., there are better way to earn money, shortage of manpower, dangerous area of the city, mismanagement by the city council+  , cdesc         = "Once so carefully curated, the planting beds are now overgrown and choked with weeds. The recently imposed high taxes make the traditional ways of life in space unsustainable."  -- also explains the gangs elsewhere and the motivation of adventurers to take risks (in addition to male hormones)   }-hunt = rogue  -- a scenario with strong missiles for ranged and shade for melee+hunt = rogue  -- a scenario with strong missiles for ranged and shade for melee;+              -- the human is likely to focus on melee, not having overwatch   { csymbol       = 'H'   , cname         = "Swamp biosphere"-  , cfreq         = [("caveHunt", 1)]+  , cfreq         = [(CAVE_HUNT, 1)]   , ccellSize     = DiceXY (1 `d` 2 + 5) 6   , cminPlaceSize = DiceXY 3 3  -- rarely merge vertically   , cmaxPlaceSize = DiceXY 5 5@@ -490,18 +591,17 @@   , cauxConnects  = 1%10   , cdoorChance   = 1   , copenChance   = 0-  , cextraStairs  = 0   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 5 `d` 10-  , citemFreq     = [ ("common item", 30)-                    , ("any arrow", 400), ("harpoon", 300), ("explosive", 50) ]-  , cplaceFreq    = [("brawl", 50), ("shootout", 100)]+  , citemNum      = 6 `d` 10+  , citemFreq     = [(IK.COMMON_ITEM, 30), (ANY_ARROW, 60), (HARPOON, 30)]+  , cplaceFreq    = [(BRAWL, 50), (SHOOTOUT, 100)]   , cpassable     = True-  , cdefTile      = "huntSetLit"  -- much more water than in shootoutSetLit-  , cdarkCorTile  = "dirt Lit"-  , clitCorTile   = "dirt Lit"-  , cwallTile     = "openableWall"+  , cdefTile      = HUNT_SET_LIT  -- much more water than in shootoutSetLit+  , cdarkCorTile  = DIRT_LIT+  , clitCorTile   = DIRT_LIT+  , cwallTile     = OPENABLE_WALL+  , cmaxStairsNum = 0   , cstairFreq    = []   , cstairAllowed = []   , cdesc         = "Mangrove trees and murky water, inspired by a habitat now eradicated from Earth."@@ -512,7 +612,7 @@                 -- lots of small lights to cross, to have some risks   { csymbol       = 'E'   , cname         = "Red Collar Bros den"  -- tension rises; non-official name-  , cfreq         = [("caveEscape", 1)]+  , cfreq         = [(CAVE_ESCAPE, 1)]   , ccellSize     = DiceXY (1 `d` 3 + 6) 7   , cminPlaceSize = DiceXY 5 4  -- rarely merge   , cmaxPlaceSize = DiceXY 9 9  -- bias towards larger lamp areas@@ -520,28 +620,29 @@   , cnightOdds    = 51  -- always night   , cauxConnects  = 2  -- many lit trails, so easy to aim   , cmaxVoid      = 1%100-  , cextraStairs  = 0   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 6 `d` 8-  , citemFreq     = [ ("common item", 30), ("gem", 150)-                    , ("weak arrow", 500), ("harpoon", 400)-                    , ("explosive", 100) ]-  , cplaceFreq    = [("escape", 1)]+  , citemNum      = 8 `d` 8+  , citemFreq     = [ (IK.COMMON_ITEM, 30), (STARTING_ARMOR, 100)+                    , (LIGHT_ATTENUATOR, 300), (GEM, 400)+                    , (WEAK_ARROW, 400), (HARPOON, 200), (IK.EXPLOSIVE, 200) ]+  , cplaceFreq    = [(ESCAPE, 1)]   , cpassable     = True-  , cdefTile      = "escapeSetDark"-  , cdarkCorTile  = "safeTrailLit"  -- let trails give off light-  , clitCorTile   = "safeTrailLit"-  , cwallTile     = "openableWall"-  , cescapeFreq   = [("escape outdoor down", 1)]+  , cdefTile      = ESCAPE_SET_DARK+  , cdarkCorTile  = SAFE_TRAIL_LIT  -- let trails give off light+  , clitCorTile   = SAFE_TRAIL_LIT+  , cwallTile     = OPENABLE_WALL+  , cescapeFreq   = [(OUTDOOR_ESCAPE_DOWN, 1)]+  , cmaxStairsNum = 0   , cstairFreq    = []   , cstairAllowed = []+  , cskip         = [0]  -- don't start heroes on exit   , cdesc         = "Graffiti scrawls across the walls and the heavy scents of stimulants hang in the air."   } zoo = rogue  -- few lights and many solids, to help the less numerous heroes   { csymbol       = 'Z'   , cname         = "Municipal zoo in flames"  -- non-official adjective-  , cfreq         = [("caveZoo", 1)]+  , cfreq         = [(CAVE_ZOO, 1)]   , ccellSize     = DiceXY (1 `d` 4 + 7) 8   , cminPlaceSize = DiceXY 4 4  -- don't merge   , cmaxPlaceSize = DiceXY 14 7@@ -551,18 +652,18 @@   , cmaxVoid      = 1%20   , cdoorChance   = 7%10   , copenChance   = 9%10-  , cextraStairs  = 0   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 7 `d` 8-  , citemFreq     = [ ("common item", 100), ("light source", 1000)-                    , ("starting weapon", 1000) ]-  , cplaceFreq    = [("zoo", 1)]+  , citemNum      = 9 `d` 8+  , citemFreq     = [ (IK.COMMON_ITEM, 100), (LIGHT_ATTENUATOR, 1000)+                    , (STARTING_ARMOR, 500), (STARTING_WEAPON, 1000) ]+  , cplaceFreq    = [(ZOO, 1)]   , cpassable     = True-  , cdefTile      = "zooSetDark"-  , cdarkCorTile  = "safeTrailLit"  -- let trails give off light-  , clitCorTile   = "safeTrailLit"-  , cwallTile     = "openableWall"+  , cdefTile      = ZOO_SET_DARK+  , cdarkCorTile  = SAFE_TRAIL_LIT  -- let trails give off light+  , clitCorTile   = SAFE_TRAIL_LIT+  , cwallTile     = OPENABLE_WALL+  , cmaxStairsNum = 0   , cstairFreq    = []   , cstairAllowed = []   , cdesc         = "Leaping flames illuminate the cages; not all are empty."@@ -579,25 +680,25 @@                 -- and the level is big enough for all that   { csymbol       = 'M'   , cname         = "Ravaged spaceport"  -- non-official adjective-  , cfreq         = [("caveAmbush", 1)]+  , cfreq         = [(CAVE_AMBUSH, 1)]   , ccellSize     = DiceXY 11 6   , cminPlaceSize = DiceXY 9 10  -- merge vertically   , cmaxPlaceSize = DiceXY 40 30  -- allow hangars and shuttles-  , cdarkOdds     = 0+  , cdarkOdds     = 51  -- all dark to spread loot evenly   , cnightOdds    = 51  -- always night   , cauxConnects  = 1%10  -- few lit trails, so hard to aim-  , cextraStairs  = 0   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 5 `d` 8-  , citemFreq     = [ ("common item", 30)-                    , ("any arrow", 400), ("harpoon", 300), ("explosive", 50) ]-  , cplaceFreq    = [("ambush", 1)]+  , citemNum      = 10 `d` 10+  , citemFreq     = [ (IK.COMMON_ITEM, 30), (MERCENARY_AMMO, 200)+                    , (HARPOON, 300), (IK.EXPLOSIVE, 50) ]+  , cplaceFreq    = [(AMBUSH, 1)]   , cpassable     = True-  , cdefTile      = "ambushSetDark"-  , cdarkCorTile  = "trailLit"  -- let trails give off light-  , clitCorTile   = "trailLit"-  , cwallTile     = "openableWall"+  , cdefTile      = AMBUSH_SET_DARK+  , cdarkCorTile  = TRAIL_LIT  -- let trails give off light+  , clitCorTile   = TRAIL_LIT+  , cwallTile     = OPENABLE_WALL+  , cmaxStairsNum = 0   , cstairFreq    = []   , cstairAllowed = []   , cdesc         = "Scarred walls and ransacked lockers show the total breakdown of order."  -- seems related to the abandoned farm; perhaps distantly to the existence of the gangs; more closely to the mystery of the lost and found space cruiser and various parties interested in it@@ -608,7 +709,7 @@ battle = rogue  -- few lights and many solids, to help the less numerous heroes   { csymbol       = 'B'   , cname         = "Old industrial plant"-  , cfreq         = [("caveBattle", 1)]+  , cfreq         = [(CAVE_BATTLE, 1)]   , ccellSize     = DiceXY (5 `d` 3 + 11) 7   , cminPlaceSize = DiceXY 4 4   , cmaxPlaceSize = DiceXY 9 7@@ -618,53 +719,51 @@   , cmaxVoid      = 1%20   , cdoorChance   = 2%10   , copenChance   = 9%10-  , cextraStairs  = 0   , chidden       = 0   , cactorFreq    = []-  , citemNum      = 5 `d` 8-  , citemFreq     = [("common item", 100), ("light source", 200)]-  , cplaceFreq    = [("battle", 50), ("rogue", 50)]+  , citemNum      = 6 `d` 8+  , citemFreq     = [(IK.COMMON_ITEM, 100), (LIGHT_ATTENUATOR, 200)]+  , cplaceFreq    = [(BATTLE, 50), (ROGUE, 50)]   , cpassable     = True-  , cdefTile      = "battleSetDark"-  , cdarkCorTile  = "safeTrailLit"  -- let trails give off light-  , clitCorTile   = "safeTrailLit"-  , cwallTile     = "openableWall"+  , cdefTile      = BATTLE_SET_DARK+  , cdarkCorTile  = SAFE_TRAIL_LIT  -- let trails give off light+  , clitCorTile   = SAFE_TRAIL_LIT+  , cwallTile     = OPENABLE_WALL   , cfenceApart   = True  -- ensures no cut-off parts from collapsed+  , cmaxStairsNum = 0   , cstairFreq    = []   , cstairAllowed = []   , cdesc         = "Huge machines stand silent and powerless in the dark."   } safari1 = brawl   { cname         = "Hunam habitat"-  , cfreq         = [("caveSafari1", 1)]+  , cfreq         = [(CAVE_SAFARI_1, 1)]   , cminPlaceSize = DiceXY 5 3-  , cextraStairs  = 1-  , cstairFreq    = [ ("outdoor walled staircase", 20)-                    , ("outdoor closed staircase", 80)-                    , ("outdoor tiny staircase", 1) ]-  , cstairAllowed = []+  , cstairFreq    = [ (OUTDOOR_WALLED_STAIRCASE, 20)+                    , (OUTDOOR_CLOSED_STAIRCASE, 80)+                    , (OUTDOOR_TINY_STAIRCASE, 1) ]+  , cskip         = [0]   , cdesc         = "\"DLC 1. Hunams scavenge in a forest in their usual disgusting way.\""   } safari2 = escape  -- lamps instead of trees, but ok, it's only a simulation   { cname         = "Deep into the jungle"-  , cfreq         = [("caveSafari2", 1)]-  , cextraStairs  = 1+  , cfreq         = [(CAVE_SAFARI_2, 1)]+  , cmaxStairsNum = 1   , cescapeFreq   = []-  , cstairFreq    = [ ("outdoor walled staircase", 20)-                    , ("outdoor closed staircase", 80)-                    , ("outdoor tiny staircase", 1) ]-  , cstairAllowed = []+  , cstairFreq    = [ (OUTDOOR_WALLED_STAIRCASE, 20)+                    , (OUTDOOR_CLOSED_STAIRCASE, 80)+                    , (OUTDOOR_TINY_STAIRCASE, 1) ]+  , cskip         = [0]   , cdesc         = "\"DLC 2. In the dark pure heart of the jungle noble animals roam freely.\""   } safari3 = zoo  -- glass rooms, but ok, it's only a simulation   { cname         = "Jungle in flames"-  , cfreq         = [("caveSafari3", 1)]+  , cfreq         = [(CAVE_SAFARI_3, 1)]   , cminPlaceSize = DiceXY 5 4-  , cescapeFreq   = [("escape outdoor down", 1)]-  , cextraStairs  = 1-  , cstairFreq    = [ ("outdoor walled staircase", 20)-                    , ("outdoor closed staircase", 80)-                    , ("outdoor tiny staircase", 1) ]-  , cstairAllowed = []+  , cescapeFreq   = [(OUTDOOR_ESCAPE_DOWN, 1)]+  , cmaxStairsNum = 1+  , cstairFreq    = [ (OUTDOOR_WALLED_STAIRCASE, 20)+                    , (OUTDOOR_CLOSED_STAIRCASE, 80)+                    , (OUTDOOR_TINY_STAIRCASE, 1) ]   , cdesc         = "\"DLC 3. Jealous hunams set jungle on fire and flee.\""   }
GameDefinition/Content/ItemKind.hs view
@@ -1,1888 +1,2840 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)--- This file is a part of the computer game Allure of the Stars--- and is released under the terms of the GNU Affero General Public License.--- For license and copyright information, see the file LICENSE.------ | Item definitions.-module Content.ItemKind-  ( content, items, otherItemContent-  ) where--import Prelude ()--import Game.LambdaHack.Core.Prelude--import Content.ItemKindActor-import Content.ItemKindBlast-import Content.ItemKindEmbed-import Content.ItemKindOrgan-import Content.ItemKindTemporary-import Content.RuleKind-import Game.LambdaHack.Content.ItemKind-import Game.LambdaHack.Content.RuleKind-import Game.LambdaHack.Core.Dice-import Game.LambdaHack.Definition.Ability-import Game.LambdaHack.Definition.Color-import Game.LambdaHack.Definition.Defs-import Game.LambdaHack.Definition.Flavour--content :: [ItemKind]-content = items ++ otherItemContent--otherItemContent :: [ItemKind]-otherItemContent = embeds ++ actors ++ organs ++ blasts ++ temporaries--items :: [ItemKind]-items =-  [sandstoneRock, dart, spike, spike2, slingStone, slingBullet, paralizingProj, harpoon, harpoon2, net, light1, light2, light3, blanket, flaskTemplate, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, potionTemplate, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, potion11, potion12, fragmentationBomb, concussionBomb, flashBomb, firecrackerBomb, ediblePlantTemplate, ediblePlant1, ediblePlant2, ediblePlant3, ediblePlant4, ediblePlant5, ediblePlant6, ediblePlant7, scrollTemplate, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, jumpingPole, sharpeningTool, seeingItem, motionScanner, gorget, necklaceTemplate, necklace1, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, necklace10, imageItensifier, sightSharpening, ringTemplate, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, armorLeather, armorLeather2, armorMail, gloveFencing, gloveGauntlet, gloveJousting, hatUshanka, capReinforced, helmArmored, buckler, shield, shield2, shield3, dagger, daggerDropBestWeapon, hammerTemplate, hammer1, hammer2, hammer3, hammerParalyze, hammerSpark, sword, swordImpress, swordNullify, halberd, halberd2, halberdPushActor, wandTemplate, wand1, gemTemplate, gem1, gem2, gem3, gem4, gem5, currencyTemplate, currency]-  -- Allure-specific-  ++ [needle, needleSleep, constructionHooter, wasteContainer, spotlight, scrollAd1, blowtorch]--sandstoneRock,    dart, spike, spike2, slingStone, slingBullet, paralizingProj, harpoon, harpoon2, net, light1, light2, light3, blanket, flaskTemplate, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, potionTemplate, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, potion11, potion12, fragmentationBomb, concussionBomb, flashBomb, firecrackerBomb, ediblePlantTemplate, ediblePlant1, ediblePlant2, ediblePlant3, ediblePlant4, ediblePlant5, ediblePlant6, ediblePlant7, scrollTemplate, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, jumpingPole, sharpeningTool, seeingItem, motionScanner, gorget, necklaceTemplate, necklace1, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, necklace10, imageItensifier, sightSharpening, ringTemplate, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, armorLeather, armorLeather2, armorMail, gloveFencing, gloveGauntlet, gloveJousting, hatUshanka, capReinforced, helmArmored, buckler, shield, shield2, shield3, dagger, daggerDropBestWeapon, hammerTemplate, hammer1, hammer2, hammer3, hammerParalyze, hammerSpark, sword, swordImpress, swordNullify, halberd, halberd2, halberdPushActor, wandTemplate, wand1, gemTemplate, gem1, gem2, gem3, gem4, gem5, currencyTemplate, currency :: ItemKind--- Allure-specific-needle, needleSleep, constructionHooter, wasteContainer, spotlight, scrollAd1, blowtorch :: ItemKind---- Keep the dice rolls and sides in aspects small so that not too many--- distinct items are generated (for display in item lore and for narrative--- impact ("oh, I found the more powerful of the two variants of the item!",--- instead of "hmm, I found one of the countless variants, a decent one").--- In particular, for unique items, unless they inherit aspects from--- a standard item, permit only a couple possible variants.--- This is especially important if an item kind has multiple random aspects.--- Instead multiply dice results, e.g., (1 `d` 3) * 5 instead of 1 `d` 15.------ Beware of non-periodic non-weapon durable items with beneficial effects--- and low timeout -- AI will starve applying such an item incessantly.---- * Item group symbols, from Angband and variants--symbolProjectile, _symbolLauncher, symbolLight, symbolTool, symbolSpecial, symbolGold, symbolNecklace, symbolRing, symbolPotion, symbolFlask, symbolScroll, symbolTorsoArmor, symbolMiscArmor, _symbolClothes, symbolShield, symbolPolearm, symbolEdged, symbolHafted, symbolWand, _symbolStaff, symbolFood :: Char--symbolProjectile = rsymbolProjectile standardRules  -- '{'-_symbolLauncher  = '}'-symbolLight      = '('-symbolTool       = ')'-symbolSpecial    = '*'  -- don't overuse, because it clashes with projectiles-symbolGold       = '$'  -- also gems-symbolNecklace   = '"'-symbolRing       = '='-symbolPotion     = '!'  -- concoction, bottle, jar, vial, canister-symbolFlask      = '!'-symbolScroll     = '?'  -- book, note, tablet, remote, chip, card-symbolTorsoArmor = '['-symbolMiscArmor  = '['-_symbolClothes   = '['-symbolShield     = ']'-symbolPolearm    = '/'-symbolEdged      = '|'-symbolHafted     = '\\'-symbolWand       = '-'  -- magical rod, transmitter, pistol, rifle, instrument-_symbolStaff     = '_'  -- scanner-symbolFood       = ','  -- also body part; distinct from floor: not middle dot---- * Generic items, for any epoch (some may be removed for Allure)---- ** Thrown weapons--sandstoneRock = ItemKind-  { isymbol  = symbolProjectile-  , iname    = "ceramic foam splinter"-  , ifreq    = [ ("sandstone rock", 1)-               , ("unreported inventory", 1) ]  -- too weak to spam-  , iflavour = zipPlain [Green]-  , icount   = 1 + 1 `d` 2  -- > 1, to let AI ignore sole pieces-  , irarity  = [(1, 50), (10, 1)]-  , iverbHit = "swat"-  , iweight  = 300-  , idamage  = 1 `d` 1-  , iaspects = [ AddSkill SkHurtMelee $ -16 * 5-               , SetFlag Fragile-               , toVelocity 70 ] -- not dense, irregular-  , ieffects = []-  , idesc    = "A light, irregular lump of ceramic foam used in construction."-  , ikit     = []-  }-dart = ItemKind-  { isymbol  = symbolProjectile-  , iname    = "billiard ball"-  , ifreq    = [("common item", 100), ("any arrow", 50), ("weak arrow", 50)]-  , iflavour = zipPlain [White]-  , icount   = 1 + 4 `dL` 5-  , irarity  = [(1, 15), (10, 5)]-  , iverbHit = "strike"-  , iweight  = 170-  , idamage  = 1 `d` 1-  , iaspects = [AddSkill SkHurtMelee $ (-15 + 1 `d` 2 + 1 `dL` 3) * 5]-                 -- only good against leather-  , ieffects = []-  , idesc    = "Ideal shape, size and weight for throwing."-  , ikit     = []-  }-spike = ItemKind-  { isymbol  = symbolProjectile-  , iname    = "steak knife"-  , ifreq    = [("common item", 100), ("any arrow", 50), ("weak arrow", 50)]-  , iflavour = zipPlain [Cyan]-  , icount   = 1 + 4 `dL` 5-  , irarity  = [(1, 10), (10, 10)]-  , iverbHit = "nick"-  , iweight  = 100-  , idamage  = 2 `d` 1-  , iaspects = [ AddSkill SkHurtMelee $ (-10 + 1 `d` 2 + 1 `dL` 3) * 5-                   -- heavy vs armor-               , SetFlag MinorEffects-               , toVelocity 70 ]  -- hitting with tip costs speed-  , ieffects = [ Explode "single spark"  -- when hitting enemy-               , OnSmash (Explode "single spark") ]  -- at wall hit-      -- this results in a wordy item synopsis, but it's OK, the spark really-      -- is useful in some situations, not just a flavour-  , idesc    = "Not particularly well balanced, but with a laser-sharpened titanium alloy tip and blade."-  , ikit     = []-  }-spike2 = spike-  { iname    = "heavy steak knife"-  , ifreq    = [("common item", 2), ("any arrow", 1), ("weak arrow", 1)]-  , icount   = 6 `dL` 5-  , iverbHit = "penetrate"-  , iweight  = 150-  , idamage = 4 `d` 1-  , iaspects = [ AddSkill SkHurtMelee $ (-10 + 1 `d` 2 + 1 `dL` 3) * 5-               , SetFlag MinorEffects-               , Odds (10 * 1 `dL` 10) [] [toVelocity 70] ]-                   -- at deep levels sometimes even don't limit velocity-  , idesc    = "Old, slightly discoloured, probably from a genuine steel. A heavy and surprisingly well balanced prop from a posh restaurant."  -- the theme of pre-modern things being more solid and intimidating-  }-slingStone = ItemKind-  { isymbol  = symbolProjectile-  , iname    = "steel hex nut"-  , ifreq    = [("common item", 5), ("any arrow", 100)]-  , iflavour = zipPlain [Blue]-  , icount   = 1 + 3 `dL` 4-  , irarity  = [(1, 1), (10, 20)]-  , iverbHit = "clobber"-  , iweight  = 200-  , idamage  = 1 `d` 1-  , iaspects = [ AddSkill SkHurtMelee $ (-10 + 1 `d` 2 + 1 `dL` 3) * 5-                   -- heavy, to bludgeon through armor-               , SetFlag MinorEffects-               , toVelocity 150 ]-  , ieffects = [ Explode "single spark"  -- when hitting enemy-               , OnSmash (Explode "single spark") ]  -- at wall hit-  , idesc    = "A large hexagonal fastening nut; due to its angular shape, securely lodging in the pouch of a makeshift string and cloth sling."-  , ikit     = []-  }-slingBullet = ItemKind-  { isymbol  = symbolProjectile-  , iname    = "bearing ball"-  , ifreq    = [("common item", 5), ("any arrow", 100)]-  , iflavour = zipPlain [BrBlack]-  , icount   = 1 + 6 `dL` 4-  , irarity  = [(1, 1), (10, 15)]-  , iverbHit = "slug"-  , iweight  = 28-  , idamage  = 1 `d` 1-  , iaspects = [ AddSkill SkHurtMelee $ (-17 + 1 `d` 2 + 1 `dL` 3) * 5-                   -- not too good against armor-               , ToThrow $ ThrowMod 200 100 2 ]  -- piercing-  , ieffects = []-  , idesc    = "Small but heavy bearing ball. Thanks to its size and shape, it doesn't snag when released from the makeshift sling's pouch. Minimal friction enable it to pierce through flesh when fast enough initially."  -- we lie, it doesn't slow down in our model; but it stops piercing alright-  , ikit     = []-  }---- ** Exotic thrown weapons---- Identified, because shape (and name) says it all. Detailed aspects id by use.--- This is an extremely large value for @Paralyze@. Normally for such values--- we should instead use condition that disables (almost) all stats,--- except @SkWait@, so that the player can switch leader and not be--- helpless nor experience instadeath (unless his party is 1-person--- or the actor is isolated, but that's usually player's fault).-paralizingProj = ItemKind-  { isymbol  = symbolProjectile-  , iname    = "can"-  , ifreq    = [("common item", 100), ("can of sticky foam", 1)]-  , iflavour = zipPlain [Magenta]-  , icount   = 1 `dL` 4-  , irarity  = [(5, 5), (10, 20)]-  , iverbHit = "glue"-  , iweight  = 1000-  , idamage  = 1 `d` 1-  , iaspects = [ ELabel "of sticky foam"-               , AddSkill SkHurtMelee $ -14 * 5-               , SetFlag Lobable, SetFlag Fragile-               , toVelocity 70 ]  -- unwieldy-  , ieffects = [Paralyze 15, OnSmash (Explode "glue") ]-  , idesc    = "A can of liquid, fast-setting construction foam."-  , ikit     = []-  }-harpoon = ItemKind-  { isymbol  = symbolProjectile-  , iname    = "harpoon"-  , ifreq    = [("curious item", 100), ("harpoon", 100), ("museum", 100)]-  , iflavour = zipPlain [Brown]-  , icount   = 1 `dL` 5-  , irarity  = [(7, 5), (10, 5)]-  , iverbHit = "hook"-  , iweight  = 750-  , idamage  = 5 `d` 1-  , iaspects = [AddSkill SkHurtMelee $ (-10 + 1 `d` 2 + 1 `dL` 3) * 5]-  , ieffects = [ PullActor (ThrowMod 200 50 1)  -- 1 step, fast-               , Yell ]  -- yell, because brutal-  , idesc    = "A display piece harking back to the Earth's oceanic tourism heyday. The cruel, barbed head lodges in its victim so painfully that the weakest tug of the thin line sends the victim flying."-  , ikit     = []-  }-harpoon2 = harpoon-  { iname    = "heavy harpoon"-  , ifreq    = [("curious item", 5), ("harpoon", 2)]-  , icount   = 2 `dL` 5-  , iweight  = 1000-  , idamage  = 10 `d` 1-  , idesc    = "A sharpened cargo-hook with high-tension cord."  -- could use some explanation of why it breaks when thrown, especially that's it's not apparent-  }-net = ItemKind-  { isymbol  = symbolProjectile-  , iname    = "net"-  , ifreq    = [("common item", 100)]-  , iflavour = zipPlain [BrGreen]-  , icount   = 1 `dL` 3-  , irarity  = [(5, 5), (10, 7)]-  , iverbHit = "entangle"-  , iweight  = 1000-  , idamage  = 2 `d` 1-  , iaspects = [AddSkill SkHurtMelee $ -14 * 5]-  , ieffects = [ toOrganBad "slowed" (3 + 1 `d` 3)-               , DropItem maxBound 1 CEqp "torso armor"-                   -- only one of each kind is dropped, because no rubbish-                   -- in this group and so no risk of exploit-               , SendFlying (ThrowMod 100 50 1) ]  -- 1 step; painful-  , idesc    = "A large synthetic fibre net with weights affixed along the edges. Entangles armor and restricts movement."-  , ikit     = []-  }---- ** Lights--light1 = ItemKind-  { isymbol  = symbolLight-  , iname    = "torch"-  , ifreq    = [ ("common item", 100), ("light source", 100)-               , ("wooden torch", 1) ]-  , iflavour = zipPlain [Brown]-  , icount   = 1 `dL` 4-  , irarity  = [(1, 30), (4, 1)]-  , iverbHit = "scorch"-  , iweight  = 1000-  , idamage  = 0-  , iaspects = [ AddSkill SkShine 3, AddSkill SkSight (-2)-                   -- not only flashes, but also sparks,-                   -- so unused by AI due to the mixed blessing-               , SetFlag Lobable, SetFlag Equipable-               , EqpSlot EqpSlotShine ]-                   -- not Fragile; reusable flare-  , ieffects = [Burn 1]-  , idesc    = "A puttering torch improvised with polymer sheets soaked in lubricant on a stick."-  , ikit     = []-  }-light2 = ItemKind-  { isymbol  = symbolLight-  , iname    = "oil lamp"-  , ifreq    = [("common item", 100), ("light source", 100)]-  , iflavour = zipPlain [BrYellow]-  , icount   = 1 `dL` 2-  , irarity  = [(6, 10)]-  , iverbHit = "burn"-  , iweight  = 1500-  , idamage  = 1 `d` 1-  , iaspects = [ AddSkill SkShine 3, AddSkill SkSight (-1)-               , SetFlag Lobable, SetFlag Fragile, SetFlag Equipable-               , EqpSlot EqpSlotShine ]-  , ieffects = [ Burn 1-               , toOrganBad "pacified" (2 + 1 `d` 2)-               , OnSmash (Explode "burning oil 2") ]-  , idesc    = "A restaurant table glass lamp filled with plant oil feeding a slender wick."-  , ikit     = []-  }-light3 = ItemKind-  { isymbol  = symbolLight-  , iname    = "brass lantern"-  , ifreq    = [("common item", 100), ("museum", 100), ("light source", 20)]-  , iflavour = zipPlain [Red]-  , icount   = 1-  , irarity  = [(6, 1), (10, 4)]-  , iverbHit = "burn"-  , iweight  = 3000-  , idamage  = 2 `d` 1-  , iaspects = [ AddSkill SkShine 4, AddSkill SkSight (-1)-               , SetFlag Lobable, SetFlag Fragile, SetFlag Equipable-               , EqpSlot EqpSlotShine ]-  , ieffects = [ Burn 1-               , toOrganBad "pacified" (4 + 1 `d` 2)-               , OnSmash (Explode "burning oil 4") ]-  , idesc    = "Very old, very bright and very heavy lantern made of hand-polished brass."-  , ikit     = []-  }-blanket = ItemKind-  { isymbol  = symbolLight-  , iname    = "mineral fibre blanket"-  , ifreq    = [("common item", 100), ("light source", 100), ("blanket", 1)]-  , iflavour = zipPlain [BrBlack]-  , icount   = 1-  , irarity  = [(1, 1)]  -- not every playthrough needs one-  , iverbHit = "swoosh"-  , iweight  = 1000-  , idamage  = 0-  , iaspects = [ AddSkill SkShine (-10)-               , AddSkill SkArmorMelee 2, AddSkill SkMaxCalm 5-               , SetFlag Lobable, SetFlag Equipable ]-                   -- not Fragile; reusable douse implement;-                   -- douses torch, lamp and lantern in one action,-                   -- both in equipment and when thrown at the floor-  , ieffects = []-  , idesc    = "Flame-retardant synthetic fibres."-  , ikit     = []-  }---- ** Exploding consumables, often intended to be thrown.---- Not identified, because they are perfect for the id-by-use fun,--- due to effects. They are fragile and upon hitting the ground explode--- for effects roughly corresponding to their normal effects.--- Whether to hit with them or explode them close to the target--- is intended to be an interesting tactical decision.------ Flasks are often not natural; maths, magic, distillery.--- In fact, they just cover all conditions, except those for stats.------ There is no flask nor condition of Calm depletion,--- because Calm reduced often via combat, etc.--flaskTemplate = ItemKind-  { isymbol  = symbolFlask-  , iname    = "flask"-  , ifreq    = [("flask unknown", 1)]-  , iflavour = zipGlassPlain darkCol ++ zipGlassFancy darkCol-               ++ zipLiquid darkCol-                 -- ++ zipPlain darkCol ++ zipFancy darkCol-  , icount   = 1 `dL` 3-  , irarity  = [(1, 7), (10, 3)]-  , iverbHit = "splash"-  , iweight  = 500-  , idamage  = 0-  , iaspects = [ HideAs "flask unknown", SetFlag Lobable, SetFlag Fragile-               , toVelocity 50 ]  -- oily, bad grip-  , ieffects = []-  , idesc    = "A flask of oily liquid of a suspect color. Something seems to be moving inside. Double dose causes twice longer effect."-  , ikit     = []-  }-flask1 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , icount   = 1 `dL` 5-  , irarity  = [(10, 10)]-  , iaspects = ELabel "of strength renewal brew"-               : iaspects flaskTemplate-  , ieffects = [ toOrganGood "strengthened" (20 + 1 `d` 5)-               , toOrganNoTimer "regenerating"-               , OnSmash (Explode "dense shower") ]-  }-flask2 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , iaspects = ELabel "of weakness brew"-               : iaspects flaskTemplate-  , ieffects = [ toOrganBad "weakened" (20 + 1 `d` 5)-               , OnSmash (Explode "sparse shower") ]-  }-flask3 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , iaspects = ELabel "of melee protective balm"-               : iaspects flaskTemplate-  , ieffects = [ toOrganGood "protected from melee" (20 + 1 `d` 5)-               , OnSmash (Explode "melee protective balm") ]-  }-flask4 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , iaspects = ELabel "of ranged protective balm"-               : iaspects flaskTemplate-  , ieffects = [ toOrganGood "protected from ranged" (20 + 1 `d` 5)-               , OnSmash (Explode "ranged protective balm") ]-  }-flask5 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , iaspects = ELabel "of fluorescent paint"-               : iaspects flaskTemplate-  , ieffects = [ toOrganBad "painted" (20 + 1 `d` 5)-               , OnSmash (Explode "fluorescent paint") ]-  }-flask6 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , irarity  = [(1, 1)]  -- not every playthrough needs one-  , iaspects = ELabel "of resolution"-               : iaspects flaskTemplate-  , ieffects = [ toOrganGood "resolute" (500 + 1 `d` 200)  -- long, for scouting-               , RefillCalm 60  -- not to make it a drawback, via @calmEnough@-               , OnSmash (Explode "resolution dust") ]-  }-flask7 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , icount   = 1  -- too powerful en masse-  , iaspects = ELabel "of haste brew"-               : iaspects flaskTemplate-  , ieffects = [ toOrganGood "hasted" (20 + 1 `d` 5)-               , OnSmash (Explode "haste spray") ]-  }-flask8 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , iaspects = ELabel "of eye drops"-               : iaspects flaskTemplate-  , ieffects = [ toOrganGood "far-sighted" (40 + 1 `d` 10)-               , OnSmash (Explode "eye drop") ]-  }-flask9 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , irarity  = [(10, 2)]  -- not very useful right now-  , iaspects = ELabel "of smelly concoction"-               : iaspects flaskTemplate-  , ieffects = [ toOrganGood "keen-smelling" (40 + 1 `d` 10)-               , Detect DetectActor 10  -- make it at least slightly useful-               , OnSmash (Explode "smelly droplet") ]-  }-flask10 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , irarity  = [(10, 2)]  -- not very useful right now-  , iaspects = ELabel "of cat tears"-               : iaspects flaskTemplate-  , ieffects = [ toOrganGood "shiny-eyed" (40 + 1 `d` 10)-               , OnSmash (Explode "eye shine") ]-  }-flask11 = flaskTemplate-  { iname    = "bottle"-  , ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , icount   = 1 `d` 3  -- the only one sometimes giving away its identity-  , iaspects = ELabel "of whiskey"-               : iaspects flaskTemplate-  , ieffects = [ toOrganGood "drunk" (20 + 1 `d` 5)-               , Burn 1, RefillHP 3, Yell-               , OnSmash (Explode "whiskey spray") ]-  }-flask12 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , icount   = 1-  , iaspects = ELabel "of bait cocktail"-               : iaspects flaskTemplate-  , ieffects = [ toOrganGood "drunk" (20 + 1 `d` 5)-               , Burn 1, RefillHP 3  -- risky exploit possible, good-               , Summon "mobile animal" 1-               , OnSmash (Summon "mobile animal" 1)-               , OnSmash Impress  -- mildly useful when thrown-               , OnSmash (Explode "waste") ]-  }--- The player has full control over throwing the flask at his party,--- so he can milk the explosion, so it has to be much weaker, so a weak--- healing effect is enough. OTOH, throwing a harmful flask at many enemies--- at once is not easy to arrange, so these explosions can stay powerful.-flask13 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , irarity  = [(1, 2), (10, 12)]-  , iaspects = ELabel "of regeneration brew"-               : iaspects flaskTemplate-  , ieffects = [ toOrganGood "rose-smelling" (80 + 1 `d` 20)-               , toOrganNoTimer "regenerating"-               , toOrganNoTimer "regenerating"  -- x2-               , OnSmash (Explode "youth sprinkle") ]-  }-flask14 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , iaspects = ELabel "of poison"-               : iaspects flaskTemplate-  , ieffects = [ toOrganNoTimer "poisoned", toOrganNoTimer "poisoned"  -- x2-               , OnSmash (Explode "poison cloud") ]-  }-flask15 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , irarity  = [(10, 4)]-  , iaspects = ELabel "of slow resistance"-               : iaspects flaskTemplate-  , ieffects = [ toOrganNoTimer "slow resistant"-               , OnSmash (Explode "anti-slow mist") ]-  }-flask16 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , irarity  = [(10, 4)]-  , iaspects = ELabel "of poison resistance"-               : iaspects flaskTemplate-  , ieffects = [ toOrganNoTimer "poison resistant"-               , OnSmash (Explode "antidote mist") ]-  }-flask17 = flaskTemplate-  { ifreq    = [("common item", 100), ("explosive", 100), ("any vial", 100)]-  , iaspects = ELabel "of calamity"-               : iaspects flaskTemplate-  , ieffects = [ toOrganNoTimer "poisoned"-               , toOrganBad "weakened" (20 + 1 `d` 5)-               , toOrganBad "painted" (20 + 1 `d` 5)-               , OnSmash (Explode "glass hail") ]  -- enough glass to cause that-  }---- Potions are often natural, including natural stats.--- They appear deeper than most flasks. Various configurations of effects.--- A different class of effects is on scrolls and mechanical items.--- Some are shared.--potionTemplate = ItemKind-  { isymbol  = symbolPotion-  , iname    = "vial"-  , ifreq    = [("potion unknown", 1)]-  , iflavour = zipLiquid brightCol ++ zipPlain brightCol ++ zipFancy brightCol-  , icount   = 1 `dL` 3-  , irarity  = [(1, 10), (10, 6)]-  , iverbHit = "splash"-  , iweight  = 200-  , idamage  = 0-  , iaspects = [ HideAs "potion unknown", SetFlag Lobable, SetFlag Fragile-               , toVelocity 50 ]  -- oily, bad grip-  , ieffects = []-  , idesc    = "A vial of bright, frothing concoction. The best that nature has to offer."-  , ikit     = []-  }-potion1 = potionTemplate-  { ifreq    = [("common item", 100), ("potion", 100), ("any vial", 100)]-  , icount   = 3 `dL` 1  -- very useful, despite appearances-  , iaspects = ELabel "of rose water"-               : iaspects potionTemplate-  , ieffects = [ Impress, toOrganGood "rose-smelling" (80 + 1 `d` 20)-               , OnSmash ApplyPerfume, OnSmash (Explode "fragrance") ]-  }-potion2 = potionTemplate-  { ifreq    = [("curious item", 100), ("any vial", 100)]-  , icount   = 1-  , irarity  = [(5, 8), (10, 8)]-  , iaspects = [ SetFlag Unique, ELabel "of Attraction"-               , SetFlag Precious, SetFlag Lobable, SetFlag Fragile-               , toVelocity 50 ]  -- identified-  , ieffects = [ Dominate-               , toOrganGood "hasted" (20 + 1 `d` 5)-               , OnSmash (Explode "pheromone")-               , OnSmash (Explode "haste spray") ]-  , idesc    = "The liquid fizzes with energy."-  }-potion3 = potionTemplate-  { ifreq    = [("common item", 100), ("potion", 100), ("any vial", 100)]-  , ieffects = [ RefillHP 5, DropItem 1 maxBound COrgan "poisoned"-               , OnSmash (Explode "healing mist") ]-  }-potion4 = potionTemplate-  { ifreq    = [("common item", 100), ("potion", 100), ("any vial", 100)]-  , irarity  = [(1, 6), (10, 10)]-  , ieffects = [ RefillHP 10, DropItem maxBound maxBound COrgan "condition"-               , OnSmash (Explode "healing mist 2") ]-  }-potion5 = potionTemplate-  -- needs to be common to show at least a portion of effects-  { ifreq    = [("common item", 100), ("potion", 100), ("any vial", 100)]-  , icount   = 3 `dL` 1  -- always as many as possible on this level-                         -- without giving away potion identity-  , irarity  = [(1, 12)]-  , ieffects = [ OneOf [ RefillHP 10, RefillHP 5, Burn 5-                       , DropItem 1 maxBound COrgan "poisoned"-                       , toOrganGood "strengthened" (20 + 1 `d` 5) ]-               , OnSmash (OneOf [ Explode "dense shower"-                                , Explode "sparse shower"-                                , Explode "melee protective balm"-                                , Explode "ranged protective balm"-                                , Explode "fluorescent paint" ]) ]-  }-potion6 = potionTemplate-  -- needs to be common to show at least a portion of effects-  { ifreq    = [("common item", 100), ("potion", 100), ("any vial", 100)]-  , icount   = 3 `dL` 1-  , irarity  = [(10, 10)]-  , ieffects = [ Impress-               , OneOf [ RefillHP 20, RefillHP 10, Burn 10-                       , DropItem 1 maxBound COrgan "poisoned"-                       , toOrganGood "hasted" (20 + 1 `d` 5)-                       , toOrganBad "impatient" (2 + 1 `d` 2) ]-               , OnSmash (OneOf [ Explode "healing mist 2"-                                , Explode "wounding mist"-                                , Explode "distressing odor"-                                , Explode "impatient mist"-                                , Explode "haste spray"-                                , Explode "slowness mist"-                                , Explode "fragrance"-                                , Explode "violent flash" ]) ]-  }-potion7 = potionTemplate-  { iname    = "ampoule"  -- filled with semi-stabilized high explosive liquid-  , ifreq    = [("common item", 100), ("potion", 100), ("any vial", 100)]-  , icount   = 3 `dL` 1-  , ieffects = [ DropItem 1 maxBound COrgan "condition"-               , OnSmash (Explode "violent concussion") ]-      -- not fragmentation nor glass hail, because not enough glass-  }-potion8 = potionTemplate-  { ifreq    = [("curious item", 100), ("any vial", 100)]-  , icount   = 1-  , irarity  = [(10, 5)]-  , iaspects = [ SetFlag Unique, ELabel "of Love"-               , SetFlag Precious, SetFlag Lobable, SetFlag Fragile-               , toVelocity 50 ]  -- identified-  , ieffects = [ RefillHP 60, RefillCalm (-60)-               , toOrganGood "rose-smelling" (80 + 1 `d` 20)-               , OnSmash (Explode "healing mist 2")-               , OnSmash (Explode "distressing odor") ]-  , idesc    = "Perplexing swirls of intense, compelling colour."-  }-potion9 = potionTemplate-  { ifreq    = [("common item", 100), ("potion", 100), ("any vial", 100)]-  , irarity  = [(10, 5)]-  , iaspects = ELabel "of grenadier focus"-               : iaspects potionTemplate-  , ieffects = [ toOrganGood "more projecting" (40 + 1 `d` 10)-               , toOrganBad "pacified" (5 + 1 `d` 3)-                   -- the malus has to be weak, or would be too good-                   -- when thrown at foes-               , OnSmash (Explode "more projecting dew")-               , OnSmash (Explode "pacified mist") ]-  , idesc    = "Thick, sluggish fluid with violently-bursting bubbles."-  }-potion10 = potionTemplate-  { ifreq    = [("common item", 100), ("potion", 100), ("any vial", 100)]-  , irarity  = [(10, 8)]-  , iaspects = ELabel "of frenzy"-               : iaspects potionTemplate-  , ieffects = [ Yell-               , toOrganGood "strengthened" (20 + 1 `d` 5)-               , toOrganBad "retaining" (5 + 1 `d` 3)-               , toOrganBad "frenzied" (40 + 1 `d` 10)-               , OnSmash (Explode "dense shower")-               , OnSmash (Explode "retaining mist")-               , OnSmash (Explode "retaining mist") ]-  }-potion11 = potionTemplate-  { ifreq    = [("common item", 100), ("potion", 100), ("any vial", 100)]-  , irarity  = [(10, 8)]-  , iaspects = ELabel "of panic"-               : iaspects potionTemplate-  , ieffects = [ RefillCalm (-30)-               , toOrganGood "hasted" (20 + 1 `d` 5)-               , toOrganBad "weakened" (20 + 1 `d` 5)-               , toOrganBad "withholding" (10 + 1 `d` 5)-               , OnSmash (Explode "haste spray")-               , OnSmash (Explode "sparse shower")-               , OnSmash (Explode "withholding mist") ]-  }-potion12 = potionTemplate-  { ifreq    = [("common item", 100), ("potion", 100), ("any vial", 100)]-  , irarity  = [(10, 8)]-  , iaspects = ELabel "of quicksilver"-               : iaspects potionTemplate-  , ieffects = [ toOrganGood "hasted" (20 + 1 `d` 5)-               , toOrganBad "blind" (10 + 1 `d` 5)-               , toOrganBad "immobile" (5 + 1 `d` 5)-               , OnSmash (Explode "haste spray")-               , OnSmash (Explode "iron filing")-               , OnSmash (Explode "immobile mist") ]-  }---- ** Explosives, with the only effect being @Explode@--fragmentationBomb = ItemKind-  { isymbol  = symbolProjectile-  , iname    = "hand bomb"-      -- improvised bomb filled with iron pellets, nuts, cut nails;-      -- deflagration, not detonation, so large mass and hard container-      -- required not to burn harmlessly; improvised short fuze;-      -- can't be more powerful or would fracture the spaceship's hull-  , ifreq    = [("common item", 100), ("explosive", 200)]-  , iflavour = zipPlain [Red]-  , icount   = 1 `dL` 5  -- many, because not very intricate-  , irarity  = [(5, 8), (10, 5)]-  , iverbHit = "thud"-  , iweight  = 3000  -- low velocity due to weight-  , idamage  = 1 `d` 1  -- heavy and hard-  , iaspects = [SetFlag Lobable, SetFlag Fragile]-  , ieffects = [ Explode "focused fragmentation"-               , OnSmash (Explode "violent fragmentation") ]-  , idesc    = "Shards of brittle metal packed around an explosive core."-  , ikit     = []-  }-concussionBomb = fragmentationBomb-  { iname    = "canister"-      -- slightly stabilized liquid explosive in a soft container, hence-      -- no fragmentation, but huge shock wave despite small size and lack-      -- of strong container to build up pressure; indoors help the shock wave;-      -- unstable enough that no fuze required (or simple electric fuse?);-      -- that's the most potent explosive (a detonating one) to be found-      -- and only in small quantities, due to depressurization hazard-  , iflavour = zipPlain [Magenta]-  , iverbHit = "bonk"-  , iweight  = 400-  , idamage  = 0-  , iaspects = [ SetFlag Lobable, SetFlag Fragile-               , toVelocity 70 ]  -- flappy and so slow-  , ieffects = [ Explode "focused concussion"-               , OnSmash (Explode "violent concussion") ]-  , idesc    = "Avoid sudden movements."-  }--- Not flashbang, because powerful bang without fragmentation is harder--- to manufacture (requires an oxidizer and steel canister with holes).--- The bang would also paralyze and/or lower the movement skill--- (out of balance due to ear trauma).-flashBomb = fragmentationBomb-  { iname    = "powder tube"  -- filled with magnesium flash powder-  , iflavour = zipPlain [BrYellow]  -- avoid @BrWhite@; looks wrong in dark-  , iverbHit = "flash"-  , iweight  = 400-  , idamage  = 0-  , iaspects = [ SetFlag Lobable, SetFlag Fragile-               , toVelocity 70 ]  -- bad shape for throwing-  , ieffects = [Explode "focused flash", OnSmash (Explode "violent flash")]-  , idesc    = "For dramatic entrances and urgent exits."-  }-firecrackerBomb = fragmentationBomb-  { iname = "roll"  -- not fireworks, as they require outdoors-  , iflavour = zipPlain [BrMagenta]-  , irarity  = [(1, 5), (5, 6)]  -- a toy, if deadly-  , iverbHit = "crack"  -- a pun, matches the verb from "ItemKindBlast"-  , iweight  = 1000-  , idamage  = 0-  , iaspects = [SetFlag Lobable, SetFlag Fragile]-  , ieffects = [Explode "firecracker", OnSmash (Explode "firecracker")]-  , idesc    = "String and paper, concealing a deadly surprise."-  }---- ** Non-exploding consumables, not specifically designed for throwing---- Foods require only minimal apply skill to consume. Many animals can eat them.--ediblePlantTemplate = ItemKind-  { isymbol  = symbolFood-  , iname    = "edible plant"-  , ifreq    = [("edible plant unknown", 1)]-  , iflavour = zipPlain stdCol-  , icount   = 1 `dL` 5-  , irarity  = [(1, 12), (10, 6)]  -- let's feed the animals-  , iverbHit = "thump"-  , iweight  = 50-  , idamage  = 0-  , iaspects = [ HideAs "edible plant unknown"-               , toVelocity 30 ]  -- low density, often falling apart-  , ieffects = []-  , idesc    = "Withered but fragrant bits of a colorful plant. Taste tolerably and break down easily, but only eating may reveal the full effects."-  , ikit     = []-  }-ediblePlant1 = ediblePlantTemplate-  { iname    = "enhanced berry"-  , ifreq    = [("common item", 100), ("edible plant", 100)]-  , ieffects = [RefillHP 1, toOrganBad "immobile" (5 + 1 `d` 5)]-  }-ediblePlant2 = ediblePlantTemplate-  { iname    = "frayed fungus"-  , ifreq    = [("common item", 100), ("edible plant", 100)]-  , ieffects = [toOrganNoTimer "poisoned"]-  }-ediblePlant3 = ediblePlantTemplate-  { iname    = "thick leaf"-  , ifreq    = [("common item", 100), ("edible plant", 100)]-  , ieffects = [DropItem 1 maxBound COrgan "poisoned"]-  }-ediblePlant4 = ediblePlantTemplate-  { iname    = "reconfigured fruit"-  , ifreq    = [("common item", 100), ("edible plant", 100)]-  , ieffects = [toOrganBad "blind" (10 + 1 `d` 10)]-  }-ediblePlant5 = ediblePlantTemplate-  { iname    = "fragrant herb"-  , ifreq    = [("common item", 100), ("edible plant", 100)]-  , icount   = 1 `dL` 9-  , irarity  = [(1, 12), (10, 5)]-  , iaspects = ELabel "of lethargy"-               : iaspects ediblePlantTemplate-  , ieffects = [ toOrganBad "slowed" (20 + 1 `d` 5)-               , toOrganNoTimer "regenerating"-               , toOrganNoTimer "regenerating"  -- x2-               , RefillCalm 5 ]-  }-ediblePlant6 = ediblePlantTemplate-  { iname    = "dull flower"-  , ifreq    = [("common item", 100), ("edible plant", 100)]-  , ieffects = [PutToSleep]-  }-ediblePlant7 = ediblePlantTemplate-  { iname    = "spicy bark"-  , ifreq    = [("common item", 100), ("edible plant", 100)]-  , ieffects = [InsertMove 20, toOrganBad "frenzied" (40 + 1 `d` 10)]-  }---- These require high apply skill to consume.--scrollTemplate = ItemKind-  { isymbol  = symbolScroll-  , iname    = "chip"-  , ifreq    = [("scroll unknown", 1)]-  , iflavour = zipFancy stdCol ++ zipPlain stdCol-  , icount   = 1 `dL` 3-  , irarity  = [(1, 14), (10, 7)]-  , iverbHit = "thump"-  , iweight  = 20-  , idamage  = 0-  , iaspects = [ HideAs "scroll unknown"-               , toVelocity 30 ]  -- too small-  , ieffects = []-  , idesc    = "A generic, disposable chip, capable of a one-time holo-display. Some of these also contain a one-time password authorizing a particular spaceship's infrastructure transition. Nobody knows how the infrastructure might respond after so many years."-  , ikit     = []-  }-scroll1 = scrollTemplate-  { ifreq    = [("curious item", 100), ("any scroll", 100)]-  , icount   = 1-  , irarity  = [(5, 9), (10, 9)]  -- mixed blessing, so found early for a unique-  , iaspects = [SetFlag Unique, ELabel "of Reckless Beacon"]-               ++ iaspects scrollTemplate-  , ieffects = [Summon "hero" 1, Summon "mobile animal" (2 + 1 `d` 2)]-  , idesc    = "This industrial, wide-spectrum alarm broadcaster, if over-amped for a single powerful blast, should be able to cut through the interference and reach any lost crew members, giving them enough positional information to locate us."-  }-scroll2 = scrollTemplate-  { ifreq    = [("curious item", 100), ("any scroll", 100)]-  , irarity  = [(1, 6), (10, 2)]-  , ieffects = [Ascend True]-  }-scroll3 = scrollTemplate-  -- needs to be common to show at least a portion of effects-  { ifreq    = [("common item", 100), ("any scroll", 100)]-  , icount   = 3 `dL` 1-  , irarity  = [(1, 14)]-  , ieffects = [OneOf [ Teleport 5, Paralyze 10, InsertMove 30-                      , Detect DetectEmbed 12, Detect DetectHidden 20 ]]-  }-scroll4 = scrollTemplate-  -- needs to be common to show at least a portion of effects-  { ifreq    = [("common item", 100), ("any scroll", 100)]-  , icount   = 3 `dL` 1-  , irarity  = [(10, 14)]-  , ieffects = [ Impress-               , OneOf [ Teleport 20, Ascend False, Ascend True-                       , Summon "hero" 1, Summon "mobile animal" $ 1 `d` 2-                       , Detect DetectLoot 20  -- the most useful of detections-                       , CreateItem CGround "common item" timerNone ] ]-  }-scroll5 = scrollTemplate-  { ifreq    = [("common item", 100), ("any scroll", 100)]-  , irarity  = [(1, 6)]  -- powerful, but low counts at the depths it appears on-  , ieffects = [InsertMove $ 20 + 1 `dL` 20]-  }-scroll6 = scrollTemplate-  { ifreq    = [("common item", 100), ("any scroll", 100)]-  , icount   = 3 `dL` 1-  , irarity  = [(1, 20)]  -- uncommon deep down, where all is known-  , iaspects = ELabel "of scientific explanation"-               : iaspects scrollTemplate-  , ieffects = [Composite [Identify, RefillCalm 10]]-  , idesc    = "The most pressing existential concerns are met with a deeply satisfying scientific answer."-  }-scroll7 = scrollTemplate-  { ifreq    = [("common item", 100), ("any scroll", 100)]-  , irarity  = [(10, 20)]  -- at endgame a crucial item may be missing-  , iaspects = ELabel "of molecular reconfiguration"-               : iaspects scrollTemplate-  , ieffects = [Composite [PolyItem, Explode "firecracker"]]-  }-scroll8 = scrollTemplate-  { ifreq    = [("curious item", 100), ("any scroll", 100)]-  , icount   = 1-  , irarity  = [(10, 12)]-  , iaspects = [SetFlag Unique, ELabel "of Skeleton Key"]-               ++ iaspects scrollTemplate-  , ieffects = [Summon "hero" 1]-  , idesc    = "This is a security lock chip that opens all doors in the area, including the hatch to a nearby closet, bulging from the blows of one of our lost crew members."-  }-scroll9 = scrollTemplate-  { ifreq    = [("common item", 100), ("any scroll", 100)]-  , irarity  = [(10, 4)]  -- powerful, even if not ideal; scares newbies-  , ieffects = [Detect DetectAll 20]-  }-scroll10 = scrollTemplate-  { ifreq    = [("common item", 100), ("any scroll", 100)]-  , iaspects = ELabel "of cue interpretation"-               : iaspects scrollTemplate-  , ieffects = [Detect DetectActor 20]-  }-scroll11 = scrollTemplate-  { ifreq    = [("common item", 100), ("any scroll", 100)]-  , irarity  = [(10, 11)]-  , ieffects = [PushActor (ThrowMod 400 200 1)]  -- 8 steps, 4 turns-  }-scroll12 = scrollTemplate-  { ifreq    = [("common item", 100), ("any scroll", 100)]-  , irarity  = [(10, 15)]-  , iaspects = ELabel "of molecular duplication"-               : iaspects scrollTemplate-  , ieffects = [DupItem]-  }-scroll13 = scrollTemplate-  { ifreq    = [("common item", 100), ("any scroll", 100)]-  , irarity  = [(10, 15)]-  , iaspects = ELabel "of surface reconfiguration"-               : iaspects scrollTemplate-  , ieffects = [RerollItem]-  }---- ** Assorted tools--jumpingPole = ItemKind-  { isymbol  = symbolWand-  , iname    = "jumping pole"-  , ifreq    = [("common item", 100)]-  , iflavour = zipPlain [White]-  , icount   = 1-  , irarity  = [(1, 3)]-  , iverbHit = "prod"-  , iweight  = 10000-  , idamage  = 0-  , iaspects = [ Timeout $ (2 + 1 `d` 2 - 1 `dL` 2) * 5-               , SetFlag Durable ]-  , ieffects = [toOrganGood "hasted" 1]-                 -- safe for AI, because it speeds up, so when AI applies it-                 -- again and again, it gets its time back and is not stuck;-                 -- in total, the explorations speed is unchanged,-                 -- but it's useful when fleeing in the dark to make distance-                 -- and when initiating combat, so it's OK that AI uses it-  , idesc    = "Makes you vulnerable at take-off, but then you are free like a bird."-  , ikit     = []-  }-sharpeningTool = ItemKind-  { isymbol  = symbolTool-  , iname    = "honing steel"-  , ifreq    = [("common item", 100)]-  , iflavour = zipPlain [Blue]-  , icount   = 1-  , irarity  = [(10, 10)]-  , iverbHit = "smack"-  , iweight  = 400-  , idamage  = 0-  , iaspects = [ AddSkill SkHurtMelee $ (1 `dL` 7) * 5-               , SetFlag Equipable, EqpSlot EqpSlotHurtMelee ]-  , ieffects = []-  , idesc    = "Originally used for realigning and sharpening dulled edges of kitchen knives in the local restaurants. Now it saves lives by keeping your melee weapons keen and true before each skirmish."-  , ikit     = []-  }-seeingItem = ItemKind-  { isymbol  = symbolFood-  , iname    = "visual sensor"-  , ifreq    = [("common item", 100)]-  , iflavour = zipPlain [Red]-  , icount   = 1-  , irarity  = [(1, 2)]-  , iverbHit = "gaze at"-  , iweight  = 500-  , idamage  = 0-  , iaspects = [ Timeout 3-               , AddSkill SkSight 10  -- a spyglass for quick wields-               , AddSkill SkMaxCalm 30  -- to diminish clipping sight by Calm-               , AddSkill SkShine 2  -- to lit corridors when flying-               , SetFlag Periodic ]-  , ieffects = [ Detect DetectActor 20  -- rare enough-               , Explode "single spark"-               , toOrganNoTimer "poisoned"  -- really can't be worn-               , Summon "mobile robot" 1 ]-  , idesc    = "An oversize visual sensor freshly torn out of some unfortunate robot. It still sends a clear picture to unidentified receivers, even though the coolant liquid seeps from the seized servos and many internal contacts spark loose."-  , ikit     = []-  }-motionScanner = ItemKind-  { isymbol  = symbolTool-  , iname    = "handheld sonar"-  , ifreq    = [("common item", 100), ("add nocto 1", 20)]-  , iflavour = zipPlain [Green]-  , icount   = 1-  , irarity  = [(5, 2)]-  , iverbHit = "ping"-  , iweight  = 1000-  , idamage  = 0-  , iaspects = [ AddSkill SkNocto 1-               , AddSkill SkArmorMelee (-15 + (1 `dL` 3) * 5)-               , AddSkill SkArmorRanged (-15 + (1 `dL` 3) * 5)-               , SetFlag Equipable, EqpSlot EqpSlotMiscBonus ]-  , ieffects = []-  , idesc    = "Portable underwater echolocator overdriven to scan dark corridors at the cost of emitting loud pings."-  , ikit     = []-  }---- ** Periodic jewelry---- Morally these are the aspects, but we also need to add a fake @Timeout@,--- to let clients know that the not identified item is periodic jewelry.-iaspects_necklaceTemplate :: [Aspect]-iaspects_necklaceTemplate =-  [ HideAs "necklace unknown"-  , SetFlag Periodic, SetFlag Precious, SetFlag Equipable-  , toVelocity 50 ]  -- not dense enough-gorget = necklaceTemplate-  { iname    = "Old Gorget"-  , ifreq    = [("common item", 25), ("treasure", 25), ("museum", 100)]-  , iflavour = zipFancy [BrCyan]  -- looks exactly the same as one of necklaces,-                                  -- but it's OK, it's an artifact-  , iaspects = [ SetFlag Unique-               , Timeout $ 5 - 1 `dL` 4-               , AddSkill SkArmorMelee 3, AddSkill SkArmorRanged 2-               , SetFlag Durable ]-               ++ iaspects_necklaceTemplate-  , ieffects = [RefillCalm 1]-  , 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 the wearer."-  }--- Not identified, because id by use, e.g., via periodic activations. Fun.-necklaceTemplate = ItemKind-  { isymbol  = symbolNecklace-  , iname    = "necklace"-  , ifreq    = [("necklace unknown", 1)]-  , iflavour = zipFancy stdCol ++ zipPlain brightCol-  , icount   = 1-  , irarity  = [(4, 3), (10, 4)]-  , iverbHit = "whip"-  , iweight  = 30-  , idamage  = 0-  , iaspects = Timeout 1000000-                 -- fake, needed to display "charging"; the timeout itself-                 -- won't be displayed thanks to periodic; as a side-effect,-                 -- it can't be activated until identified, which is better-                 -- than letting the player try to activate before the real-                 -- cooldown is over and waste turn-               : iaspects_necklaceTemplate-  , ieffects = []-  , idesc    = "Tingling, rattling chain of flat encrusted links. Eccentric millionaires are known to hide their highly personalized body augmentation packs in bulky jewelry pieces such as these."-  , ikit     = []-  }-necklace1 = necklaceTemplate-  { ifreq    = [("curious item", 100), ("any jewelry", 100)]-  , irarity  = [(3 * 10/15, 0), (4 * 10/15, 1), (10, 3)]-                 -- prevents camping on lvl 3-  , iaspects = [ SetFlag Unique, ELabel "of Spur Life"-               , Timeout $ (4 - 1 `dL` 3) * 10-                   -- priceless, so worth the long wait-               , SetFlag Durable ]-               ++ iaspects_necklaceTemplate-  , ieffects = [RefillHP 1, RefillCalm (-5)]-  , idesc    = "This awkward chain, when worn on bare skin, frequently emits mild but highly annoying electric shocks, which apparently stimulate tissue regeneration even in distant parts of the body. A part of the surprising effectiveness of this unique artifact may stem from the desperation of the patients to be quickly healed enough to take it off."-  }--- no necklace2 of Live Bait, wasteContainer too similar-necklace3 = necklaceTemplate-  { ifreq    = [("common item", 100), ("any jewelry", 100)]-  , iaspects = [ ELabel "of fearful listening"-               , Timeout ((1 + 1 `d` 2) * 10)-               , AddSkill SkHearing 2 ]-               ++ iaspects_necklaceTemplate-  , ieffects = [ Detect DetectActor 10  -- can be applied; destroys the item-               , RefillCalm (-40) ]-  }-necklace4 = necklaceTemplate-  { ifreq    = [("common item", 100), ("any jewelry", 100)]-  , iaspects = Timeout ((3 + 1 `d` 3 - 1 `dL` 3) * 2)-               : iaspects_necklaceTemplate-  , ieffects = [Teleport $ 3 `d` 2]-  }-necklace5 = necklaceTemplate-  { ifreq    = [("common item", 100), ("any jewelry", 100)]-  , iaspects = [ ELabel "of escape"-               , Timeout $ (7 - 1 `dL` 5) * 10 ]-               ++ iaspects_necklaceTemplate-  , ieffects = [ Teleport $ 14 + 3 `d` 3  -- can be applied; destroys the item-               , Detect DetectExit 20-               , Yell ]  -- drawback when used for quick exploring-  , idesc    = "A supple chain that slips through your fingers."-  }-necklace6 = necklaceTemplate-  { ifreq    = [("common item", 100), ("any jewelry", 100)]-  , iaspects = Timeout (1 + (1 `d` 3) * 2)-               : iaspects_necklaceTemplate-  , ieffects = [PushActor (ThrowMod 100 50 1)]  -- 1 step, slow-                  -- the @50@ is only for the case of very light actor, etc.-  }-necklace7 = necklaceTemplate-  { ifreq    = [("curious item", 100), ("any jewelry", 100)]-  , irarity  = [(10, 1)]  -- powerful and determines tactics for one actor-  , iaspects = [ SetFlag Unique, ELabel "of Overdrive"-               , Timeout 10-               , AddSkill SkMaxHP 10  -- good effects vanish when taken off-               , AddSkill SkSpeed 10-               , SetFlag Durable ]-               ++ iaspects_necklaceTemplate-  , ieffects = [ RefillCalm (-2)  -- don't spam-               , toOrganBad "pacified" 10]-                 -- The same duration as timeout, to avoid spurious messages-                 -- as well as unlimited accumulation of the duration.-                 -- Timeout lessens temptation to frequently wear and take off,-                 -- to engage in melee, which would lead to micromanagement.-  , idesc    = "This whirring augmentation pack stimulates its host beyond any medically advisable or, surely, even legally admissible levels. It can be only speculated what kind of activity it was designed for, but clearly the steady handling of melee weapons was not one of them."-  }-necklace8 = necklaceTemplate-  { ifreq    = [("common item", 100), ("any jewelry", 100)]-  , irarity  = [(4, 3)]  -- entirely optional-  , iaspects = Timeout ((1 + 1 `d` 3) * 5)-               : iaspects_necklaceTemplate-  , ieffects = [Explode "spark"]-  }-necklace9 = necklaceTemplate-  { ifreq    = [("common item", 100), ("any jewelry", 100)]-  , iaspects = Timeout ((1 + 1 `d` 3) * 5)-               : iaspects_necklaceTemplate-  , ieffects = [Explode "fragrance"]-  }-necklace10 = necklaceTemplate-  { ifreq    = [("common item", 100), ("any jewelry", 100)]-  , iaspects = [ ELabel "of greed"-               , Timeout ((2 + 1 `d` 3) * 10) ]-               ++ iaspects_necklaceTemplate-  , ieffects = [ Detect DetectLoot 20-               , Teleport 40  -- risky-               , toOrganBad "parsimonious" (5 + 1 `d` 3) ]  -- hard to flee-  }---- ** Non-periodic jewelry--imageItensifier = ItemKind-  { isymbol  = symbolRing-  , iname    = "noctovisor"-  , ifreq    = [("treasure", 100), ("add nocto 1", 80), ("museum", 100)]-  , iflavour = zipFancy [BrGreen]-  , icount   = 1-  , irarity  = [(5, 2)]-  , iverbHit = "rattle"-  , iweight  = 700-  , idamage  = 0-  , iaspects = [ AddSkill SkNocto 1, AddSkill SkSight (-1)-               , AddSkill SkArmorMelee $ (-1 + 1 `dL` 6) * 3-               , SetFlag Precious, SetFlag Equipable-               , EqpSlot EqpSlotMiscBonus ]-  , ieffects = []-  , idesc    = "Sturdy antique night vision goggles of unknown origin. Wired to run on modern micro-cells."-  , ikit     = []-  }-sightSharpening = ringTemplate  -- small and round, so mistaken for a ring-  { iname    = "Autozoom Contact Lens"-  , ifreq    = [("treasure", 20), ("add sight", 1)]-      -- it's has to be very rare, because it's powerful and not unique,-      -- and also because it looks exactly as one of necklaces, so it would-      -- be misleading when seen on the map-  , irarity  = [(7, 1), (10, 12)]  -- low @ifreq@-  , iweight  = 50  -- heavier that it looks, due to glass-  , iaspects = [ AddSkill SkSight $ 1 + 1 `dL` 2-               , AddSkill SkHurtMelee $ (-1 + 1 `d` 3) * 3-               , EqpSlot EqpSlotSight ]-               ++ iaspects ringTemplate-  , idesc    = "Zooms on any movement, distant or close. Requires some getting used to. Never needs to be taken off."-  }--- Don't add standard effects to rings, because they go in and out--- of eqp and so activating them would require UI tedium: looking for--- them in eqp and inv or even activating a wrong item by mistake.------ By general mechanisms, due to not having effects that could identify--- them by observing the effect, rings are identified on pickup.--- That's unlike necklaces, which provide the fun of id-by-use, because they--- have effects and when the effects are triggered, they get identified.-ringTemplate = ItemKind-  { isymbol  = symbolRing-  , iname    = "ring"-  , ifreq    = [("ring unknown", 1)]-  , iflavour = zipPlain stdCol ++ zipFancy darkCol-  , icount   = 1-  , irarity  = [(10, 2)]  -- the default very low-  , iverbHit = "knock"-  , iweight  = 15-  , idamage  = 0-  , iaspects = [HideAs "ring unknown", SetFlag Precious, SetFlag Equipable]-  , ieffects = []-  , idesc    = "A sturdy ring with a softly shining eye. If it contains a body booster unit, beware of the side-effects."-  , ikit     = []-  }-ring1 = ringTemplate-  { ifreq    = [("common item", 100), ("any jewelry", 100)]-  , irarity  = [(8, 4)]-  , iaspects = [ AddSkill SkSpeed $ 1 `dL` 3, AddSkill SkMaxHP (-10)-               , EqpSlot EqpSlotSpeed ]-               ++ iaspects ringTemplate-  }-ring2 = ringTemplate-  { ifreq    = [("curious item", 100), ("any jewelry", 100)]-  , iaspects = [ SetFlag Unique, ELabel "of Rush"-               , AddSkill SkSpeed $ (1 + 1 `dL` 2) * 2-               , AddSkill SkMaxCalm (-40), AddSkill SkMaxHP (-20)-               , SetFlag Durable, EqpSlot EqpSlotSpeed ]-               ++ iaspects ringTemplate-  , idesc    = "There creator of this dangerous artifact didn't find time to document its operation. And now it's too late."-  }-ring3 = ringTemplate-  { ifreq    = [("common item", 100), ("any jewelry", 100)]-  , irarity  = [(10, 8)]-  , iaspects = [ AddSkill SkMaxHP $ 5 + (1 `d` 2 + 1 `dL` 2) * 5-               , AddSkill SkMaxCalm $ -30 + (1 `dL` 3) * 5-               , EqpSlot EqpSlotMaxHP ]-               ++ iaspects ringTemplate-  }-ring4 = ringTemplate-  { ifreq    = [("common item", 100), ("any jewelry", 100), ("museum", 100)]-  , irarity  = [(5, 1), (10, 9)]  -- needed after other rings drop Calm-  , iaspects = [ AddSkill SkMaxCalm $ 20 + (1 `dL` 4) * 5-               , EqpSlot EqpSlotMiscBonus ]-               ++ iaspects ringTemplate-  , idesc    = "Cold, solid to the touch, perfectly round, engraved with solemn, strangely comforting, worn out words."-  }-ring5 = ringTemplate-  { ifreq    = [("common item", 100), ("any jewelry", 100)]-  , irarity  = [(3, 4), (10, 8)]-  , iaspects = [ AddSkill SkHurtMelee $ (2 + 1 `d` 2 + (1 `dL` 2) * 2 ) * 3-               , AddSkill SkMaxHP $ (-3 + 1 `dL` 3) * 10-               , EqpSlot EqpSlotHurtMelee ]-               ++ iaspects ringTemplate-  }-ring6 = ringTemplate  -- weak skill per eqp slot, so can be without drawbacks-  { ifreq    = [("common item", 100), ("any jewelry", 100), ("museum", 100)]-  , irarity  = [(10, 3)]-  , iaspects = [ AddSkill SkShine 1-               , EqpSlot EqpSlotShine ]-               ++ iaspects ringTemplate-  , idesc    = "A sturdy ring with a large, shining stone."-  }-ring7 = ringTemplate-  { ifreq    = [("common item", 10), ("ring of opportunity sniper", 1)]-  , irarity  = [(10, 5)]  -- low @ifreq@-  , iaspects = [ ELabel "of opportunity sniper"-               , AddSkill SkProject 8-               , EqpSlot EqpSlotProject ]-               ++ iaspects ringTemplate-  }-ring8 = ringTemplate-  { ifreq    = [("treasure", 100), ("any jewelry", 100)]-  , iaspects = [ SetFlag Unique, ELabel "of Overwatch"-               , AddSkill SkProject 8  -- TODO: 11, but let player control-                                       -- potion throwing; see capReinforced-               , AddSkill SkMaxHP (-20)-               , SetFlag Durable, EqpSlot EqpSlotProject ]-               ++ iaspects ringTemplate-  , idesc    = "This exceptional medical contraption constantly transforms and re-injects minuscule amounts of blood serum, synthesizing powerful drugs that greatly enhance spacial awareness and focus, at the cost of weakening bodily resilience and recovery."-  }---- ** Armor--armorLeather = ItemKind-  { isymbol  = symbolTorsoArmor-  , iname    = "spacesuit breastplate"-  , ifreq    = [("common item", 100), ("torso armor", 1)]-  , iflavour = zipPlain [Brown]-  , icount   = 1-  , irarity  = [(1, 4), (10, 2)]-  , iverbHit = "thud"-  , iweight  = 7000-  , idamage  = 0-  , iaspects = [ AddSkill SkHurtMelee (-2)-               , AddSkill SkArmorMelee $ (2 + 1 `dL` 4) * 5-               , AddSkill SkArmorRanged $ (1 + 1 `dL` 2) * 3-               , SetFlag Durable, SetFlag Equipable-               , EqpSlot EqpSlotArmorMelee ]-  , ieffects = []-  , idesc    = "A hard-shell torso segment cut from a disposed off spacesuit. Well ventilated."-  , ikit     = []-  }-armorLeather2 = armorLeather  -- for now, purely flavour, for better messages-  { isymbol  = symbolMiscArmor-  , iname    = "pair"-  , ifreq    = [("common item", 100)]-  , iaspects = ELabel "of spacesuit trousers" : iaspects armorLeather-  , idesc    = "Segmented trousers for open space work, with the hermetically sealed boots cut off. Surprisingly flexible and airy, yet micro-meteorite-proof."-  }-armorMail = armorLeather-  { iname    = "bulletproof vest"-  , ifreq    = [("common item", 100), ("torso armor", 1), ("armor ranged", 50)]-  , iflavour = zipPlain [Cyan]-  , irarity  = [(6, 9), (10, 3)]-  , iweight  = 12000-  , idamage  = 0-  , iaspects = [ AddSkill SkHurtMelee (-3)-               , AddSkill SkArmorMelee $ (2 + 1 `dL` 4) * 5-               , AddSkill SkArmorRanged $ (4 + 1 `dL` 2) * 3-               , AddSkill SkOdor 2-               , SetFlag Durable, SetFlag Equipable-               , EqpSlot EqpSlotArmorRanged ]-  , ieffects = []-  , idesc    = "A civilian bulletproof vest. Discourages foes from attacking your torso, making it harder for them to land a blow. Really hard to wash due to thickness."-  }-gloveFencing = ItemKind-  { isymbol  = symbolMiscArmor-  , iname    = "construction glove"-  , ifreq    = [("common item", 100), ("misc armor", 1), ("armor ranged", 50)]-  , iflavour = zipPlain [White]-  , icount   = 1-  , irarity  = [(5, 9), (10, 9)]-  , iverbHit = "flap"-  , iweight  = 100-  , idamage  = 1 `d` 1-  , iaspects = [ AddSkill SkHurtMelee $ (2 + 1 `d` 2 + 1 `dL` 2) * 3-               , AddSkill SkArmorRanged $ (1 `dL` 2) * 3-               , SetFlag Durable, SetFlag Equipable-               , EqpSlot EqpSlotHurtMelee-               , toVelocity 50 ]  -- flaps and flutters-  , ieffects = []-  , idesc    = "A flexible construction glove from rough leather ensuring a good grip. Also, quite effective in deflecting or even catching slow projectiles."-  , ikit     = []-  }-gloveGauntlet = gloveFencing-  { iname    = "spacesuit glove"-  , ifreq    = [("common item", 100), ("misc armor", 1)]-  , iflavour = zipPlain [BrCyan]-  , irarity  = [(1, 9), (10, 3)]-  , iweight  = 300-  , idamage  = 2 `d` 1-  , iaspects = [ AddSkill SkArmorMelee $ (1 + 1 `dL` 4) * 5-               , SetFlag Durable, SetFlag Equipable-               , EqpSlot EqpSlotArmorMelee-               , toVelocity 50 ]  -- flaps and flutters-  , idesc    = "A piece of a hull maintenance spacesuit, padded and reinforced with carbon fibre."-  }-gloveJousting = gloveFencing-  { iname    = "Welding Handgear"-  , ifreq    = [("common item", 100), ("misc armor", 1)]-  , iflavour = zipFancy [BrRed]-  , irarity  = [(1, 3), (10, 3)]-  , iverbHit = "rasp"-  , iweight  = 3000-  , idamage  = 3 `d` 1-  , iaspects = [ SetFlag Unique-               , AddSkill SkHurtMelee $ (-7 + 1 `dL` 5) * 3-               , AddSkill SkArmorMelee $ (2 + 1 `d` 2 + 1 `dL` 2) * 5-               , AddSkill SkArmorRanged $ (1 + 1 `dL` 2) * 3-                 -- very random on purpose and can even be good on occasion-                 -- or when ItemRerolled enough times-               , SetFlag Durable, SetFlag Equipable-               , EqpSlot EqpSlotArmorMelee-               , toVelocity 50 ]  -- flaps and flutters-  , idesc    = "Rigid, bulky handgear embedding a welding equipment, complete with an affixed small shield and a darkened visor. Awe-inspiring."-  }-hatUshanka = ItemKind-  { isymbol  = symbolMiscArmor-  , iname    = "ushanka hat"-  , ifreq    = [("common item", 100), ("misc armor", 1)]-  , iflavour = zipPlain [Brown]-  , icount   = 1-  , irarity  = [(1, 6), (10, 2)]-  , iverbHit = "tickle"-  , iweight  = 500-  , idamage  = 0-  , iaspects = [ Timeout $ (2 + 1 `d` 2) * 3-               , AddSkill SkArmorMelee 5, AddSkill SkHearing (-10)-               , SetFlag Periodic, SetFlag Durable, SetFlag Equipable-               , EqpSlot EqpSlotArmorMelee-               , toVelocity 50 ]  -- flaps and flutters-  , ieffects = [RefillCalm 1]-  , idesc    = "Soft and warm fur. It keeps your ears warm."-  , ikit     = []-  }-capReinforced = ItemKind-  { isymbol  = symbolMiscArmor-  , iname    = "construction cap"-  , ifreq    = [("common item", 100), ("misc armor", 1)]-  , iflavour = zipPlain [BrYellow]-  , icount   = 1-  , irarity  = [(6, 9), (10, 3)]-  , iverbHit = "cut"-  , iweight  = 1000-  , idamage  = 0-  , iaspects = [ AddSkill SkArmorMelee $ (1 `d` 2) * 5-               , AddSkill SkProject 1-                   -- the brim shields against blinding by light sources, etc.-               , SetFlag Durable, SetFlag Equipable-               , EqpSlot EqpSlotProject ]-  , ieffects = []-  , idesc    = "A hard plastic shell that might soften a blow."-  , ikit     = []-  }-helmArmored = ItemKind-  { isymbol  = symbolMiscArmor-  , iname    = "spacesuit helmet"-  , ifreq    = [("common item", 100), ("misc armor", 1)]-  , iflavour = zipPlain [BrCyan]-  , icount   = 1-  , irarity  = [(6, 9), (10, 3)]-  , iverbHit = "bounce"-  , iweight  = 2000-  , idamage  = 0-  , iaspects = [ AddSkill SkArmorMelee $ (1 + 1 `dL` 4) * 5-               , AddSkill SkArmorRanged $ (2 + 1 `dL` 2) * 3  -- headshot-               , AddSkill SkHearing (-7), AddSkill SkSight (-1)-               , AddSkill SkSmell (-5)-               , SetFlag Durable, SetFlag Equipable-               , EqpSlot EqpSlotArmorRanged ]-  , ieffects = []-  , idesc    = "Blocks out everything, including your senses."-  , ikit     = []-  }---- ** Shields---- Shield doesn't protect against ranged attacks to prevent--- micromanagement: walking with shield, melee without.--- Note that AI will pick them up but never wear and will use them at most--- as a way to push itself (but they won't recharge, not being in eqp).--- Being @Meleeable@ they will not be use as weapons either.--- This is OK, using shields smartly is totally beyond AI.-buckler = ItemKind-  { isymbol  = symbolShield-  , iname    = "buckler"-  , ifreq    = [("common item", 100)]-  , iflavour = zipPlain [Blue]-  , icount   = 1-  , irarity  = [(4, 5)]-  , iverbHit = "bash"-  , iweight  = 2000-  , idamage  = 0  -- safe to be used on self-  , iaspects = [ Timeout $ (3 + 1 `d` 3 - 1 `dL` 3) * 2-               , AddSkill SkArmorMelee 40-                   -- not enough to compensate; won't be in eqp-               , AddSkill SkHurtMelee (-30)-                   -- too harmful; won't be wielded as weapon-               , SetFlag MinorEffects, SetFlag Durable, SetFlag Meleeable-               , EqpSlot EqpSlotArmorMelee-               , toVelocity 50 ]  -- unwieldy to throw-  , ieffects = [PushActor (ThrowMod 200 50 1)]  -- 1 step, fast-  , idesc    = "Heavy and unwieldy arm protection made from an outer airlock panel. Absorbs a percentage of melee damage, both dealt and sustained. Too small to intercept projectiles with."-  , ikit     = []-  }-shield = buckler-  { iname    = "shield"-  , irarity  = [(8, 4)]  -- the stronger variants add to total probability-  , iflavour = zipPlain [Green]-  , iweight  = 4000-  , idamage  = 4 `d` 1-  , iaspects = [ Timeout $ (3 + 1 `d` 3 - 1 `dL` 3) * 4-               , AddSkill SkArmorMelee 80-                   -- not enough to compensate; won't be in eqp-               , AddSkill SkHurtMelee (-70)-                   -- too harmful; won't be wielded as weapon-               , SetFlag MinorEffects, SetFlag Durable, SetFlag Meleeable-               , EqpSlot EqpSlotArmorMelee-               , toVelocity 50 ]  -- unwieldy to throw-  , ieffects = [PushActor (ThrowMod 400 50 1)]  -- 2 steps, fast-  , idesc    = "Large and unwieldy rectangle made of anti-meteorite ceramic sheet. Absorbs a percentage of melee damage, both dealt and sustained. Too heavy to intercept projectiles with."-  }-shield2 = shield-  { ifreq    = [("common item", 3 * 2), ("museum", 100)]-                  -- very low base rarity-  , iweight  = 5000-  , idamage  = 8 `d` 1-  , idesc    = "A relic of long-past wars, heavy and with a central spike."-  }-shield3 = shield2-  { ifreq    = [("common item", 1 * 2), ("museum", 10)]-                  -- very low base rarity-  , iweight  = 6000-  , idamage  = 12 `d` 1-  }---- ** Weapons--dagger = ItemKind-  { isymbol  = symbolEdged-  , iname    = "cleaver"-  , ifreq    = [("common item", 100), ("starting weapon", 200)]-  , iflavour = zipPlain [BrCyan]-  , icount   = 1-  , irarity  = [(3 * 10/15, 35), (4 * 10/15, 1)]-                 -- no weapons brought by aliens, initially, so cleaver common-  , iverbHit = "cut"-  , iweight  = 1000-  , idamage  = 6 `d` 1-  , iaspects = [ Timeout 2-               , AddSkill SkHurtMelee $ (-1 + 1 `d` 2 + 1 `dL` 2) * 3-               , AddSkill SkArmorMelee $ (1 `d` 2) * 5-                   -- very common, so don't make too random-               , SetFlag Durable, SetFlag Meleeable-               , EqpSlot EqpSlotWeaponFast-               , toVelocity 40 ]  -- ensuring it hits with the tip costs speed-  , ieffects = []-  , idesc    = "A heavy professional kitchen blade. Will do fine cutting any kind of meat and bone, as well as parrying blows. Does not penetrate deeply, but is quick to move and hard to block. Especially useful in conjunction with a larger weapon."-  , ikit     = []-  }-daggerDropBestWeapon = dagger-  { iname    = "Double Dagger"-  , ifreq    = [("treasure", 20), ("museum", 100)]-  , irarity  = [(1, 3), (10, 3)]-  , iaspects = [SetFlag Unique]-               ++ iaspects dagger-  , ieffects = [DropBestWeapon, Yell]  -- powerful and low timeout, but makes-                                       -- noise and useless against stupid foes-  , idesc    = "An antique double dagger that a focused fencer can use to catch and twist away an opponent's blade."-  }-hammerTemplate = ItemKind-  { isymbol  = symbolHafted-  , iname    = "demolition hammer"-  , ifreq    = [("hammer unknown", 1)]-  , iflavour = zipFancy [BrMagenta]  -- avoid "pink"-  , icount   = 1-  , irarity  = [(3 * 10/15, 1), (5 * 10/15, 20), (8 * 10/15, 1)]-                 -- don't make it too common on lvl 3-  , iverbHit = "club"-  , iweight  = 1600-  , idamage  = 8 `d` 1  -- we are lying about the dice here, but the dungeon-                        -- is too small and the extra-dice hammers too rare-                        -- to subdivide this identification class by dice-  , iaspects = [ HideAs "hammer unknown"-               , SetFlag Durable, SetFlag Meleeable-               , toVelocity 40 ]  -- ensuring it hits with the tip costs speed-  , ieffects = []-  , idesc    = "One of many kinds of hammers employed in construction work. The ones with completely blunt heads don't cause grave wounds, but any fitted with a long enough handle can shake and bruise even most armored foes. However, such large hammers require more time to recover after a swing. This one looks average at a quick glance."  -- if it's really the average kind, the weak kind, the description stays; if not, it's replaced with one of the descriptions below at identification time-  , ikit     = []-  }-hammer1 = hammerTemplate-  { ifreq    = [("common item", 100), ("starting weapon", 70)]-  , iaspects = [Timeout 5, EqpSlot EqpSlotWeaponBig]-               ++ iaspects hammerTemplate-  }-hammer2 = hammerTemplate-  { ifreq    = [("common item", 20), ("starting weapon", 7)]-  , iverbHit = "gouge"-  , iaspects = [Timeout 3, EqpSlot EqpSlotWeaponFast]-               ++ iaspects hammerTemplate-  , idesc    = "Upon closer inspection, this hammer turns out particularly handy and well balanced, with a narrowing, sharpened head compensating the modest size."-  }-hammer3 = hammerTemplate-  { ifreq    = [("common item", 3), ("starting weapon", 1)]-  , iverbHit = "puncture"-  , iweight  = 2400  -- weight gives it away-  , idamage  = 12 `d` 1-  , iaspects = [ Timeout 12  -- balance, or @DupItem@ would break the game-               , EqpSlot EqpSlotWeaponBig]-               ++ delete (HideAs "hammer unknown") (iaspects hammerTemplate)-  , idesc    = "This hammer sports a long metal handle that increases the momentum of the sharpened head's swing, at the cost of long recovery."-  }-hammerParalyze = hammerTemplate-  { iname    = "Concussion Hammer"-  , ifreq    = [("treasure", 20)]-  , irarity  = [(5, 1), (8, 6)]-  , iaspects = [ SetFlag Unique-               , Timeout 5-               , EqpSlot EqpSlotWeaponBig ]-               ++ iaspects hammerTemplate-  , ieffects = [Paralyze 10]-  , idesc    = "This exquisite demolition hammer with a titanium head and exceptionally long synthetic handle leaves no wall and no body standing."-  }-hammerSpark = hammerTemplate-  { iname    = "Grand Smithhammer"-  , ifreq    = [("treasure", 20), ("museum", 100)]-  , irarity  = [(5, 1), (8, 6)]-  , iweight  = 2400  -- weight gives it away-  , idamage  = 12 `d` 1-  , iaspects = [ SetFlag Unique-               , Timeout 10-               , EqpSlot EqpSlotWeaponBig-               , AddSkill SkShine 3]-               ++ delete (HideAs "hammer unknown") (iaspects hammerTemplate)-  , ieffects = [Explode "spark"]-      -- we can't use a focused explosion, because it would harm the hammer-      -- wielder as well, unlike this one-  , idesc    = "High carbon steel of this heavy old hammer doesn't yield even to the newest alloys and produces fountains of sparks in defiance."-  }-sword = ItemKind-  { isymbol  = symbolPolearm-  , iname    = "sharpened pipe"-  , ifreq    = [("common item", 100), ("starting weapon", 30)]-  , iflavour = zipPlain [BrBlue]-  , icount   = 1-  , irarity  = [(4, 1), (6, 15)]-  , iverbHit = "stab"-  , iweight  = 2000-  , idamage  = 10 `d` 1-  , iaspects = [ Timeout 7-               , SetFlag Durable, SetFlag Meleeable-               , EqpSlot EqpSlotWeaponBig-               , toVelocity 40 ]  -- ensuring it hits with the tip costs speed-  , ieffects = []-  , idesc    = "A makeshift weapon of simple design, but great potential. Hard to master, though."-  , ikit     = []-  }-swordImpress = sword-  { isymbol  = symbolEdged-  , iname    = "Master's Sword"-  , ifreq    = [("treasure", 20), ("museum", 100)]-  , irarity  = [(5, 1), (8, 6)]-  , iverbHit = "slash"-  , iaspects = [SetFlag Unique]-               ++ iaspects sword-  , ieffects = [Impress]-  , idesc    = "A particularly well-balance museum piece. It has a long history and in the right hands lends itself to impressive shows of fencing skill."-  }-swordNullify = sword-  { isymbol  = symbolEdged-  , iname    = "Roasting Rapier"-  , ifreq    = [("treasure", 20)]-  , iverbHit = "pierce"-  , irarity  = [(5, 1), (8, 6)]-  , iaspects = [SetFlag Unique, Timeout 3, EqpSlot EqpSlotWeaponFast]-               ++ (iaspects sword \\ [Timeout 7, EqpSlot EqpSlotWeaponBig])-  , ieffects = [ DropItem 1 maxBound COrgan "condition"-               , RefillCalm (-10)-               , Yell ]-  , idesc    = "A thin, acutely sharp steel blade that pierces deeply and sends its victim into abrupt, sobering shock. Originally, an exuberant hand-forged roasting implement, intentionally and wisely kept blunt."-  }-halberd = ItemKind-  { isymbol  = symbolPolearm-  , iname    = "pole cleaver"-  , ifreq    = [("common item", 100), ("starting weapon", 20)]-  , iflavour = zipPlain [BrYellow]-  , icount   = 1-  , irarity  = [(5, 0), (8, 15)]-  , iverbHit = "impale"-  , iweight  = 3000-  , idamage  = 12 `d` 1-  , iaspects = [ Timeout 10-               , AddSkill SkHurtMelee $ (-5 + 1 `dL` 3) * 5-                   -- useless against armor at game start-               , AddSkill SkArmorMelee 20-               , SetFlag Durable, SetFlag Meleeable-               , EqpSlot EqpSlotWeaponBig-               , toVelocity 20 ]  -- not balanced-  , ieffects = []-  , idesc    = "An improvised but deadly weapon made of a long, sharp kitchen knife glued and bound to a long pole. Not often one succeeds in making enough space to swing it freely, but even when stuck between terrain obstacles it blocks approaches effectively and makes using other weapons difficult, both by friends and foes."-  , ikit     = []-  }-halberd2 = halberd-  { iname    = "pollaxe"-  , ifreq    = [("common item", 3 * 3), ("starting weapon", 1)]-  , iverbHit = "carve"-  , iweight  = 4000-  , iaspects = [AddSkill SkHurtMelee $ (-6 + 1 `dL` 4) * 10]-                 -- balance, or @DupItem@ would break the game;-                 -- together with @RerollItem@, it's allowed to, though-               ++ (iaspects halberd-                   \\ [AddSkill SkHurtMelee $ (-6 + 1 `dL` 4) * 5])-  , idamage  = 18 `d` 1-  , idesc    = "A long-hafted axe: once used for maintenance, now turned to a bloodier purpose."-  }-halberdPushActor = halberd-  { iname    = "Swiss Halberd"-  , ifreq    = [("curious item", 20)]  -- not museum; reenactors-  , irarity  = [(7, 0), (9, 15)]-  , iaspects = [SetFlag Unique]-               ++ iaspects halberd-  , ieffects = [PushActor (ThrowMod 200 100 1)]  -- 2 steps, slow-  , idesc    = "A perfect replica made for a reenactor troupe, hardened, missing only some final sharpening. Versatile, with great reach and leverage. Foes are held at a distance."-  }---- ** Wands--wandTemplate = ItemKind-  { isymbol  = symbolWand-  , iname    = "injector"-  , ifreq    = [("wand unknown", 1)]-  , iflavour = zipFancy brightCol-  , icount   = 1-  , irarity  = []-  , iverbHit = "club"-  , iweight  = 300-  , idamage  = 0-  , iaspects = [ HideAs "wand unknown"-               , AddSkill SkShine 1, AddSkill SkSpeed (-1)-                   -- pulsing with power, distracts-               , SetFlag Durable-               , toVelocity 125 ]  -- sufficiently advanced tech-  , ieffects = []-  , idesc    = "Buzzing with dazzling light that shines even through appendages that handle it."-  , ikit     = []-  }-wand1 = wandTemplate-  { ifreq    = []-  , ieffects = []  -- will be: emit a cone of sound shrapnel that makes enemy cover his ears and so drop '|' and '{'-  }---- ** Treasure--gemTemplate = ItemKind-  { isymbol  = symbolGold-  , iname    = "gem"-  , ifreq    = [("gem unknown", 1), ("valuable", 100)]-  , iflavour = zipPlain $ delete BrYellow brightCol  -- natural, so not fancy-  , icount   = 1-  , irarity  = [(3, 0), (10, 24)]-  , iverbHit = "tap"-  , iweight  = 50-  , idamage  = 0-  , iaspects = [HideAs "gem unknown", SetFlag Precious]-  , ieffects = []-  , idesc    = "Precious, though useless. Worth around 100 gold grains."-  , ikit     = []-  }-gem1 = gemTemplate-  { ifreq    = [ ("treasure", 100), ("gem", 100), ("any jewelry", 100)-               , ("valuable", 100) ]-  , irarity  = [(3, 0), (6, 12), (10, 8)]-  , iaspects = [AddSkill SkShine 1, AddSkill SkSpeed (-1)]-                 -- reflects strongly, distracts; so it glows in the dark,-                 -- is visible on dark floor, but not too tempting to wear-               ++ iaspects gemTemplate-  }-gem2 = gem1-  { ifreq    = [ ("treasure", 100), ("gem", 100), ("any jewelry", 100)-               , ("valuable", 100) ]-  , irarity  = [(5, 0), (7, 25), (10, 8)]-  }-gem3 = gem1-  { ifreq    = [ ("treasure", 100), ("gem", 100), ("any jewelry", 100)-               , ("valuable", 100) ]-  , irarity  = [(7, 0), (8, 20), (10, 8)]-  }-gem4 = gem1-  { ifreq    = [ ("treasure", 100), ("gem", 100), ("any jewelry", 100)-               , ("valuable", 100) ]-  , irarity  = [(9, 0), (10, 70)]-  }-gem5 = gem1-  { isymbol  = symbolSpecial-  , iname    = "stimpack"-  , ifreq    = [ ("treasure", 100), ("gem", 25), ("any jewelry", 10)-               , ("valuable", 100) ]-  , iflavour = zipPlain [BrYellow]-  , irarity  = [(1, 40), (10, 10)]-  , iaspects = [ ELabel "of youth", SetFlag Precious  -- not hidden-               , AddSkill SkOdor (-1) ]-  , ieffects = [RefillCalm 10, RefillHP 40]-  , idesc    = "Calms, heals, invigorates, rejuvenates and smells nice. No side-effects. As valuable as precious gems, at 100 gold grains each."-  }-currencyTemplate = ItemKind-  { isymbol  = symbolGold-  , iname    = "gold grain"-  , ifreq    = [("currency unknown", 1), ("valuable", 1)]-  , iflavour = zipPlain [BrYellow]-  , icount   = 10 + 1 `d` 20 + 1 `dL` 20-  , irarity  = [(1, 25), (10, 10)]-  , iverbHit = "tap"-  , iweight  = 1-  , idamage  = 0-  , iaspects = [HideAs "currency unknown", SetFlag Precious]-  , ieffects = []-  , idesc    = "Reliably valuable in every civilized place."-  , ikit     = []-  }-currency = currencyTemplate-  { ifreq    = [("treasure", 100), ("currency", 100), ("valuable", 1)]-  , iaspects = [AddSkill SkShine 1, AddSkill SkSpeed (-1)]-               ++ iaspects currencyTemplate-  }---- * Allure-specific items--needle = ItemKind-  { isymbol  = symbolProjectile-  , iname    = "needle"-  , ifreq    = [ ("needle", 1), ("common item", 1)-                   -- marked as common to ensure can be polymorphed-               , ("unreported inventory", 1) ]  -- too weak to spam-  , iflavour = zipPlain [Blue]-  , icount   = 1 + 8 `d` 3-  , irarity  = [(1, 1)]-  , iverbHit = "prick"-  , iweight  = 3-  , idamage  = 1 `d` 1-  , iaspects = [ AddSkill SkHurtMelee $ -10 * 5-               , SetFlag Fragile  -- breaks easily despite being piercing-               , ToThrow $ ThrowMod 70 100 3 ]  -- piercing; good shape-  , ieffects = []-  , idesc    = "A long sturdy hypodermic needle ending in a dried out micro-syringe that is easy to break off. It's too thin to cause great harm, but it passes through flesh easily."-  , ikit     = []-  }-needleSleep = ItemKind-  { isymbol  = symbolProjectile-  , iname    = "tranquillizer dart"-  , ifreq    = [ ("tranquillizer dart", 1), ("common item", 1) ]-                   -- marked as common to ensure can be polymorphed-  , iflavour = zipPlain [BrBlue]-  , icount   = 1 `dL` 3-  , irarity  = [(1, 1)]-  , iverbHit = "prick"-  , iweight  = 10-  , idamage  = 1 `d` 1-  , iaspects = [ AddSkill SkHurtMelee $ -10 * 5-               , SetFlag Fragile-               , toVelocity 70 ]  -- syringe blocks piercing; slender fins-  , ieffects = [PutToSleep]-  , idesc    = "A long hypodermic needle ending in a micro-syringe with residues of the sleeping agent."-  , ikit     = []-  }-constructionHooter = necklaceTemplate-  { iname    = "construction hooter"-  , ifreq    = [ ("construction hooter", 1), ("common item", 1)-                   -- extremely rare, but dropped by decontamination chambers-               , ("unreported inventory", 1) ]-  , iflavour = zipPlain [BrRed]-  , irarity  = [(1, 1)]-  , iweight  = 1000-  , iaspects = [ AddSkill SkArmorMelee 2-               , SetFlag Durable, toVelocity 50-               , SetFlag Equipable, EqpSlot EqpSlotArmorMelee]-  , ieffects = [Yell, Summon "construction robot" 1]-  , idesc    = "An emergency hooter for alarming human personnel in case their life is in danger. Worn by construction robots around their \"neck\", where it's least exposed, but even there it needs to be heavily armored and running on its own power supply."-  }-wasteContainer = ItemKind-  { isymbol  = symbolTool-  , iname    = "waste container"-  , ifreq    = [("waste container", 1), ("unreported inventory", 1)]-  , iflavour = zipLiquid [Green]-  , icount   = 1-  , irarity  = [(1, 1)]-  , iverbHit = "spill over"-  , iweight  = 30000-  , idamage  = 0-  , iaspects = [ Timeout $ (1 `d` 2) * 30  -- robots should not summon too often-               , AddSkill SkArmorMelee 20  -- tempting-               , SetFlag Periodic, SetFlag Equipable ]-  , ieffects = [ Detect DetectLoot 20-               , Summon "mobile animal" $ 1 `dL` 2-               , Explode "waste" ]-                   -- very important effect that disables item movement-                   -- and so makes wielding seeingItem a tiny bit risky,-                   -- and so not a no-brainer-  , idesc    = "Waste recognition and utilization subsystem. Detects any stray item not registered as passenger cargo. Leaks a little."-  , ikit     = []-  }-spotlight = ItemKind-  { isymbol  = symbolLight-  , iname    = "spotlight"-  , ifreq    = [("spotlight", 1), ("unreported inventory", 1)]-  , iflavour = zipPlain [White]-  , icount   = 1-  , irarity  = [(1, 1)]-  , iverbHit = "illuminate"-  , iweight  = 3000-  , idamage  = 0-  , iaspects = [ AddSkill SkShine 4-               , AddSkill SkHurtMelee (-3)  -- heavy and unwieldy-               , SetFlag Equipable, EqpSlot EqpSlotShine ]-  , ieffects = [Detect DetectHidden 20]-  , idesc    = "Powerful wide-beam spotlight in an unwieldy rack-mounted package. On overdrive, it can shine through thin construction surfaces, underlying fault lines."-  , ikit     = []-  }-scrollAd1 = scrollTemplate-  { ifreq    = [("common item", 100), ("any scroll", 100)]-  , icount   = 1-  , irarity  = [(1, 1)]  -- not every playthrough needs one-  , iaspects = [ELabel "of tourist guide"]-               ++ iaspects scrollTemplate-  , ieffects = [ Impress-               , toOrganGood "resolute" (500 + 1 `d` 200)-                   -- a drawback (at least initially) due to @calmEnough@-               , Explode "cruise ad hologram"-               , Detect DetectLoot 5 ]  -- short so useless most of the time-  , idesc    = "Biodegradable self-powered mini-projector displaying holographic ads and shopping hints."-  }-blowtorch = ItemKind-  { isymbol  = symbolLight-  , iname    = "blowtorch"  -- not unique, but almost never generated on floor-  , ifreq    = [("blowtorch", 1), ("valuable", 20), ("curious item", 1)]-  , iflavour = zipPlain [BrYellow]-  , icount   = 1-  , irarity  = [(1, 1)]-  , iverbHit = "scorch"-  , iweight  = 2000-  , idamage  = 0-  , iaspects = [ Timeout 4-               , AddSkill SkAlter 2-               , SetFlag Durable, SetFlag Meleeable-               , EqpSlot EqpSlotAlter ]-  , ieffects = [Burn 2, Impress]-      -- is used for melee in precedence to fists, but not to cleavers;-      -- so if player wants to hit with it, it's enough to pack other gear-  , idesc    = "A sturdy old-fashioned portable blowtorch for fine cutting or welding of metals. Rather weak, but does not require access codes to high current power outlets."+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history)+-- This file is a part of the computer game Allure of the Stars+-- and is released under the terms of the GNU Affero General Public License.+-- For license and copyright information, see the file LICENSE.+--+-- | Item definitions.+module Content.ItemKind+  ( -- * Group name patterns+    pattern HARPOON, pattern ARMOR_LOOSE, pattern CLOTHING_MISC+  , pattern COOKED_PLANT, pattern LIQUID_NITROGEN, pattern GARDENING_TOOL+  , groupNamesSingleton, groupNames+  , -- * Content+    content, items, otherItemContent+  ) where++import Prelude ()++import Game.LambdaHack.Core.Prelude++import Content.ItemKindActor+import Content.ItemKindBlast+import Content.ItemKindEmbed+import Content.ItemKindOrgan+import Content.ItemKindTemporary+import Content.RuleKind+import Game.LambdaHack.Content.ItemKind+import Game.LambdaHack.Content.RuleKind+import Game.LambdaHack.Core.Dice+import Game.LambdaHack.Definition.Ability+import Game.LambdaHack.Definition.Color+import Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Definition.Flavour++-- * Group name patterns++groupNamesSingleton :: [GroupName ItemKind]+groupNamesSingleton =+       [S_FRAGRANCE, S_SINGLE_SPARK, S_SPARK]+    ++ [FLASK_UNKNOWN, POTION_UNKNOWN, EDIBLE_PLANT_UNKNOWN, SCROLL_UNKNOWN, NECKLACE_UNKNOWN, RING_UNKNOWN, HAMMER_UNKNOWN, GEM_UNKNOWN, CURRENCY_UNKNOWN]+    ++ [S_RAG_TANGLE, S_GRASS_STITCHER, S_LADIES_FORK, S_SPADE, S_HOE]+    ++ [COOKED_PLANT_UNKNOWN]+    ++ embedsGNSingleton ++ actorsGNSingleton ++ organsGNSingleton+    ++ blastsGNSingleton ++ temporariesGNSingleton++pattern FLASK_UNKNOWN, POTION_UNKNOWN, EDIBLE_PLANT_UNKNOWN, SCROLL_UNKNOWN, NECKLACE_UNKNOWN, RING_UNKNOWN, HAMMER_UNKNOWN, GEM_UNKNOWN, CURRENCY_UNKNOWN :: GroupName ItemKind++pattern S_RAG_TANGLE, S_GRASS_STITCHER, S_LADIES_FORK, S_SPADE, S_HOE :: GroupName ItemKind++pattern COOKED_PLANT_UNKNOWN :: GroupName ItemKind++groupNames :: [GroupName ItemKind]+groupNames =+       [CRAWL_ITEM, TREASURE, ANY_SCROLL, ANY_GLASS, ANY_POTION, ANY_FLASK, EXPLOSIVE, ANY_JEWELRY, VALUABLE, UNREPORTED_INVENTORY, AQUATIC]+    ++ [HARPOON, ARMOR_LOOSE, CLOTHING_MISC]+    ++ [COOKED_PLANT, LIQUID_NITROGEN, GARDENING_TOOL, TOOL_ONLY]+    ++ embedsGN ++ actorsGN ++ organsGN ++ blastsGN++pattern HARPOON, ARMOR_LOOSE, CLOTHING_MISC :: GroupName ItemKind++pattern COOKED_PLANT, LIQUID_NITROGEN, GARDENING_TOOL, TOOL_ONLY :: GroupName ItemKind++-- The @UNKNOWN@ patterns don't need to be exported. Used internally.+-- They also represent singleton groups.+pattern FLASK_UNKNOWN = GroupName "flask unknown"+pattern POTION_UNKNOWN = GroupName "potion unknown"+pattern EDIBLE_PLANT_UNKNOWN = GroupName "edible plant unknown"+pattern SCROLL_UNKNOWN = GroupName "scroll unknown"+pattern NECKLACE_UNKNOWN = GroupName "necklace unknown"+pattern RING_UNKNOWN = GroupName "ring unknown"+pattern HAMMER_UNKNOWN = GroupName "hammer unknown"+pattern GEM_UNKNOWN = GroupName "gem unknown"+pattern CURRENCY_UNKNOWN = GroupName "currency unknown"++pattern HARPOON = GroupName "harpoon"+pattern ARMOR_LOOSE = GroupName "loose armor"+pattern CLOTHING_MISC = GroupName "miscellaneous clothing"++-- ** Allure-specific++-- Below Allure-specific definitions are interspersed among generic+-- definitions to make managing related definitions easier.++-- The @UNKNOWN@ patterns don't need to be exported. Used internally.+-- They also represent singleton groups.+pattern COOKED_PLANT_UNKNOWN = GroupName "cooked plant unknown"++pattern COOKED_PLANT = GroupName "cooked plant"+pattern LIQUID_NITROGEN = GroupName "liquid nitrogen"+pattern GARDENING_TOOL = GroupName "gardening tool"+pattern TOOL_ONLY = GroupName "tool only"++pattern S_RAG_TANGLE = GroupName "rag tangle"+pattern S_GRASS_STITCHER = GroupName "grass stitcher"+pattern S_LADIES_FORK = GroupName "ladies' fork"+pattern S_HOE = GroupName "hoe"+pattern S_SPADE = GroupName "spade"++-- * Content++content :: [ItemKind]+content = items ++ otherItemContent++otherItemContent :: [ItemKind]+otherItemContent = embeds ++ actors ++ organs ++ blasts ++ temporaries++items :: [ItemKind]+items =+  [sandstoneRock, steelScrap, needle, dart, spike, spike2, slingStone, slingBullet, needleSleep, paralizingProj, harpoon, harpoon2, harpoon3, net, fragmentationBomb, concussionBomb, flashBomb, firecrackerBomb, flaskEmpty, flaskTemplate, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, potionTemplate, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, potion11, potion12, potion13, potion14, potion15, potion16, scrollTemplate, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, scroll14, scroll15, scroll16, scrollAd1, rawMeatChunk, roastedMeatChunk, ediblePlantTemplate, ediblePlant1, ediblePlant2, ediblePlant3, ediblePlant4, ediblePlant5, ediblePlant6, ediblePlant7, ediblePlant8, cookedPlantTemplate, cookedPlant1, cookedPlant2, cookedPlant3, cookedPlant4, cookedPlant5, cookedPlant6, cookedPlant7, cookedPlant8, light1, lightDoused1, light2, lightDoused2, light3, blanket, chisel, hacksaw, adjustableSpanner, steelFile, honingSteel, whetstone, diagonalPliers, snips, loppers, boltCutter, solderingIron, duckTape, thickCord, gorget, necklaceTemplate, necklace1, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, necklace10, motionScanner, imageItensifier, sightSharpening, ringTemplate, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, ring9, ring10, armorLeather, armorLeather2, armorMail, meleeEnhancement, spacesuit, spacesuitTorn, gloveFencing, gloveGauntlet, gloveJousting, hatUshanka, capReinforced, helmArmored, heavyBoot, ragTangle, buckler, shield, shield2, shield3, blowtorch, laserSharpener, crowbar, catsPaw, shortClub, longClub, hammerTemplate, hammer1, hammer2, hammer3, hammer4, hammer5, hammerParalyze, hammerSpark, knife, daggerDropBestWeapon, dagger, sword, swordImpress, swordNullify, swordNullifySharp, halberd, oxTongue, halberdPushActor, halberdPushActorSharp, fireAxe, pollaxe, militaryKnife, militaryBaton, cattleProd, grassStitcher, ladiesFork, hoe, spade, treePruner, cleaningPole, staff, pipe, longPole, gemTemplate, gem1, gem2, gem3, gem4, gem5, currencyTemplate, currency, jumpingPole, constructionHooter, wasteContainer, spotlight, seeingItem]++sandstoneRock,    steelScrap, needle, dart, spike, spike2, slingStone, slingBullet, needleSleep, paralizingProj, harpoon, harpoon2, harpoon3, net, fragmentationBomb, concussionBomb, flashBomb, firecrackerBomb, flaskEmpty, flaskTemplate, flask1, flask2, flask3, flask4, flask5, flask6, flask7, flask8, flask9, flask10, flask11, flask12, flask13, flask14, flask15, flask16, flask17, potionTemplate, potion1, potion2, potion3, potion4, potion5, potion6, potion7, potion8, potion9, potion10, potion11, potion12, potion13, potion14, potion15, potion16, scrollTemplate, scroll1, scroll2, scroll3, scroll4, scroll5, scroll6, scroll7, scroll8, scroll9, scroll10, scroll11, scroll12, scroll13, scroll14, scroll15, scroll16, scrollAd1, rawMeatChunk, roastedMeatChunk, ediblePlantTemplate, ediblePlant1, ediblePlant2, ediblePlant3, ediblePlant4, ediblePlant5, ediblePlant6, ediblePlant7, ediblePlant8, cookedPlantTemplate, cookedPlant1, cookedPlant2, cookedPlant3, cookedPlant4, cookedPlant5, cookedPlant6, cookedPlant7, cookedPlant8, light1, lightDoused1, light2, lightDoused2, light3, blanket, chisel, hacksaw, adjustableSpanner, steelFile, honingSteel, whetstone, diagonalPliers, snips, loppers, boltCutter, solderingIron, duckTape, thickCord, gorget, necklaceTemplate, necklace1, necklace3, necklace4, necklace5, necklace6, necklace7, necklace8, necklace9, necklace10, motionScanner, imageItensifier, sightSharpening, ringTemplate, ring1, ring2, ring3, ring4, ring5, ring6, ring7, ring8, ring9, ring10, armorLeather, armorLeather2, armorMail, meleeEnhancement, spacesuit, spacesuitTorn, gloveFencing, gloveGauntlet, gloveJousting, hatUshanka, capReinforced, helmArmored, heavyBoot, ragTangle, buckler, shield, shield2, shield3, blowtorch, laserSharpener, crowbar, catsPaw, shortClub, longClub, hammerTemplate, hammer1, hammer2, hammer3, hammer4, hammer5, hammerParalyze, hammerSpark, knife, daggerDropBestWeapon, dagger, sword, swordImpress, swordNullify, swordNullifySharp, halberd, oxTongue, halberdPushActor, halberdPushActorSharp, fireAxe, pollaxe, militaryKnife, militaryBaton, cattleProd, grassStitcher, ladiesFork, hoe, spade, treePruner, cleaningPole, staff, pipe, longPole, gemTemplate, gem1, gem2, gem3, gem4, gem5, currencyTemplate, currency, jumpingPole, constructionHooter, wasteContainer, spotlight, seeingItem :: ItemKind++-- Keep the dice rolls and sides in aspects small so that not too many+-- distinct items are generated (for display in item lore and for narrative+-- impact ("oh, I found the more powerful of the two variants of the item!",+-- instead of "hmm, I found one of the countless variants, a decent one").+-- In particular, for unique items, unless they inherit aspects from+-- a standard item, permit only a couple possible variants.+-- This is especially important if an item kind has multiple random aspects.+-- Instead multiply dice results, e.g., (1 `d` 3) * 5 instead of 1 `d` 15.+--+-- Beware of non-periodic non-weapon durable items with beneficial effects+-- and low timeout -- AI will starve applying such an item incessantly.++-- * Item group symbols, from Angband and variants++symbolProjectile, _symbolLauncher, symbolLight, symbolTool, symbolSpecial, symbolGold, symbolNecklace, symbolRing, symbolPotion, symbolFlask, symbolScroll, symbolTorsoArmor, symbolMiscArmor, symbolClothes, symbolShield, symbolPolearm, symbolEdged, symbolHafted, symbolWand, _symbolStaff, symbolFood :: Char++symbolProjectile = rsymbolProjectile standardRules  -- '{'+_symbolLauncher  = '}'+symbolLight      = '('+symbolTool       = ')'+symbolSpecial    = '*'  -- don't overuse, because it clashes with projectiles+symbolGold       = '$'  -- also gems+symbolNecklace   = '"'+symbolRing       = '='+symbolPotion     = '!'  -- concoction, bottle, jar, vial, canister+symbolFlask      = '!'+symbolScroll     = '?'  -- book, note, tablet, remote, chip, card+symbolTorsoArmor = '['+symbolMiscArmor  = '['+symbolClothes    = '['+symbolShield     = ']'+symbolPolearm    = '/'+symbolEdged      = '|'+symbolHafted     = '\\'+symbolWand       = '-'  -- magical rod, transmitter, pistol, rifle, instrument+_symbolStaff     = '_'  -- scanner+symbolFood       = ','  -- also body part; distinct from floor: not middle dot++-- ** Thrown weapons++sandstoneRock = ItemKind+  { isymbol  = symbolProjectile+  , iname    = "ceramic foam splinter"+  , ifreq    = [ (S_SANDSTONE_ROCK, 1)+               , (UNREPORTED_INVENTORY, 1) ]  -- too weak to spam+  , iflavour = zipPlain [Green]+  , icount   = 1 + 1 `d` 2  -- > 1, to let AI ignore sole pieces+  , irarity  = [(1, 1)]+  , iverbHit = "swat"+  , iweight  = 300+  , idamage  = 1 `d` 1+  , iaspects = [ AddSkill SkHurtMelee $ -16 * 5+               , SetFlag Fragile+               , toVelocity 70 ] -- not dense, irregular+  , ieffects = []+  , idesc    = "A light, irregular lump of ceramic foam used in construction."+  , ikit     = []+  }+steelScrap = sandstoneRock+  { iname    = "steel scrap"+  , ifreq    = [ (STEEL_SCRAP, 1)+               , (UNREPORTED_INVENTORY, 1) ]  -- too weak to spam+  , iflavour = zipPlain [Cyan]+  , iverbHit = "grate"+  , idamage  = 2 `d` 1+  , iweight  = 700+  , idesc    = " A lump of steel scrap that can be easily bent around and pounded into a wood pole."+  }+needle = ItemKind+  { isymbol  = symbolProjectile+  , iname    = "needle"+  , ifreq    = [ (NEEDLE, 1), (COMMON_ITEM, 1)+                   -- marked as common to ensure can be polymorphed+               , (UNREPORTED_INVENTORY, 1) ]  -- too weak to spam+  , iflavour = zipPlain [Blue]+  , icount   = 1 + 8 `d` 3+  , irarity  = [(1, 1)]+  , iverbHit = "prick"+  , iweight  = 3+  , idamage  = 1 `d` 1+  , iaspects = [ AddSkill SkHurtMelee $ -10 * 5+               , ToThrow $ ThrowMod 60 100 5  -- piercing; good shape+               , SetFlag Fragile ]  -- breaks easily despite being piercing+  , ieffects = []+  , idesc    = "A long sturdy hypodermic needle ending in a dried out micro-syringe that is easy to break off. It's too thin to cause great harm, but it passes through flesh easily."+  , ikit     = []+  }+dart = ItemKind+  { isymbol  = symbolProjectile+  , iname    = "billiard ball"+  , ifreq    = [(COMMON_ITEM, 100), (ANY_ARROW, 50), (WEAK_ARROW, 50)]+  , iflavour = zipPlain [White]+  , icount   = 1 + 1 `d` 2 + 4 `dL` 5+  , irarity  = [(1, 25)]+  , iverbHit = "strike"+  , iweight  = 170+  , idamage  = 1 `d` 1+  , iaspects = [AddSkill SkHurtMelee $ (-15 + 1 `d` 2 + 1 `dL` 3) * 5]+                 -- only good against leather+  , ieffects = []+  , idesc    = "Ideal shape, size and weight for throwing."+  , ikit     = []+  }+spike = ItemKind+  { isymbol  = symbolProjectile+  , iname    = "steak knife"+  , ifreq    = [(COMMON_ITEM, 100), (ANY_ARROW, 50), (WEAK_ARROW, 50)]+  , iflavour = zipPlain [BrCyan]+  , icount   = 1 + 1 `d` 2 + 3 `dL` 5+  , irarity  = [(1, 15), (10, 10)]+  , iverbHit = "nick"+  , iweight  = 100+  , idamage  = 2 `d` 1+  , iaspects = [ AddSkill SkHurtMelee $ (-10 + 1 `d` 2 + 1 `dL` 3) * 5+                   -- heavy vs armor+               , SetFlag MinorEffects+               , toVelocity 70 ]  -- hitting with tip costs speed+  , ieffects = [ Explode S_SINGLE_SPARK  -- when hitting enemy+               , OnSmash (Explode S_SINGLE_SPARK) ]  -- at wall hit+      -- this results in a wordy item synopsis, but it's OK, the spark really+      -- is useful in some situations, not just a flavour+  , idesc    = "Not durable nor particularly well balanced, but with a laser-sharpened titanium alloy tip and blade."+  , ikit     = []+  }+spike2 = spike+  { iname    = "heavy steak knife"+  , ifreq    = [(COMMON_ITEM, 3), (ANY_ARROW, 1), (WEAK_ARROW, 1)]+  , iflavour = zipPlain [Cyan]+  , icount   = 1 + 5 `dL` 5+  , iverbHit = "penetrate"+  , iweight  = 150+  , idamage = 4 `d` 1  -- not useful for melee, because hurt skill too low+  , iaspects = [ AddSkill SkHurtMelee $ (-10 + 1 `d` 2 + 1 `dL` 3) * 5+               , SetFlag MinorEffects+               , Odds (10 * 1 `dL` 10) [] [toVelocity 70] ]+                   -- at deep levels sometimes even don't limit velocity+  , idesc    = "Old, slightly discoloured, probably from a genuine steel. A heavy and surprisingly well balanced prop from a posh restaurant. It won't survive any rough treatment, though."  -- the theme of pre-modern things being more solid and intimidating+  }+slingStone = ItemKind+  { isymbol  = symbolProjectile+  , iname    = "steel hex nut"+  , ifreq    = [(COMMON_ITEM, 5), (ANY_ARROW, 100)]+  , iflavour = zipPlain [Blue]+  , icount   = 1 + 3 `dL` 4+  , irarity  = [(8, 25)]+  , iverbHit = "clobber"+  , iweight  = 200+  , idamage  = 1 `d` 1+  , iaspects = [ AddSkill SkHurtMelee $ (-10 + 1 `d` 2 + 1 `dL` 3) * 5+                   -- heavy, to bludgeon through armor+               , SetFlag MinorEffects+               , toVelocity 150 ]+  , ieffects = [ Explode S_SINGLE_SPARK  -- when hitting enemy+               , OnSmash (Explode S_SINGLE_SPARK) ]  -- at wall hit+  , idesc    = "A large hexagonal fastening nut; due to its angular shape, securely lodging in the pouch of a makeshift string and cloth sling."+  , ikit     = []+  }+slingBullet = ItemKind+  { isymbol  = symbolProjectile+  , iname    = "bearing ball"+  , ifreq    = [(COMMON_ITEM, 5), (ANY_ARROW, 100), (MERCENARY_AMMO, 25)]+  , iflavour = zipPlain [BrBlue]+  , icount   = 1 + 6 `dL` 4+  , irarity  = [(8, 20)]+  , iverbHit = "slug"+  , iweight  = 28+  , idamage  = 1 `d` 1+  , iaspects = [ AddSkill SkHurtMelee $ (-17 + 1 `d` 2 + 1 `dL` 3) * 5+                   -- not too good against armor+               , ToThrow $ ThrowMod 200 100 2  -- piercing+               , SetFlag Fragile ]+                   -- otherwise would rarely break and the player would have+                   -- unlimited resource and would have to pick up constantly+  , ieffects = []+  , idesc    = "Small but heavy bearing ball. Thanks to its size and shape, it doesn't snag when released from the makeshift sling's pouch. Minimal friction enables it to pierce through flesh when fast enough initially. Really hard to find once thrown."  -- we lie, it doesn't slow down in our model; but it stops piercing alright+  , ikit     = []+  }++-- ** Exotic thrown weapons++needleSleep = ItemKind+  { isymbol  = symbolProjectile+  , iname    = "tranquillizer dart"+  , ifreq    = [ (TRANQUILIZER_DART, 1), (COMMON_ITEM, 1)+               , (MERCENARY_AMMO, 25) ]+                   -- marked as common to ensure can be polymorphed+  , iflavour = zipPlain [BrBlue]+  , icount   = 1 `dL` 3+  , irarity  = [(1, 1)]+  , iverbHit = "prick"+  , iweight  = 10+  , idamage  = 1 `d` 1+  , iaspects = [ AddSkill SkHurtMelee $ -10 * 5+               , SetFlag Fragile+               , toVelocity 60 ]  -- syringe blocks piercing; slender fins+  , ieffects = [PutToSleep]+  , idesc    = "A long hypodermic needle ending in a micro-syringe with residues of the sleeping agent."+  , ikit     = []+  }+-- Identified, because shape (and name) says it all. Detailed aspects id by use.+-- This is an extremely large value for @Paralyze@. Normally for such values+-- we should instead use condition that disables (almost) all stats,+-- except @SkWait@, so that the player can switch leader and not be+-- helpless nor experience instadeath (unless his party is 1-person+-- or the actor is isolated, but that's usually player's fault).+paralizingProj = ItemKind+  { isymbol  = symbolProjectile+  , iname    = "can"+  , ifreq    = [ (COMMON_ITEM, 100), (CAN_OF_STICKY_FOAM, 1)+               , (MERCENARY_AMMO, 25), (BONDING_TOOL, 1) ]+  , iflavour = zipPlain [Magenta]+  , icount   = 1 `dL` 4+  , irarity  = [(5, 5), (10, 20)]+  , iverbHit = "glue"+  , iweight  = 1000+  , idamage  = 1 `d` 1+  , iaspects = [ ELabel "of sticky foam"+               , AddSkill SkHurtMelee $ -14 * 5+               , SetFlag Lobable, SetFlag Fragile+               , toVelocity 70 ]  -- unwieldy+  , ieffects = [Paralyze 15, OnSmash (Explode S_GLUE) ]+  , idesc    = "A can of liquid, fast-setting construction foam. Often used as a glue."+  , ikit     = []+  }+harpoon = ItemKind+  { isymbol  = symbolProjectile+  , iname    = "harpoon"+  , ifreq    = [(COMMON_ITEM, 33), (HARPOON, 100), (S_HARPOON_CARGO, 1)]+  , iflavour = zipPlain [Brown]+  , icount   = 1  -- durable, so one piece lasts long+  , irarity  = [(1, 17)]+  , iverbHit = "hook"+  , iweight  = 1500  -- high damage and reusable, but one shot less via pulling+  , idamage  = 5 `d` 1+  , iaspects = [ Timeout 7+               , AddSkill SkHurtMelee $ (-4 + 1 `d` 3) * 5+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponBig ]  -- AI wields for fun despite stats+  , ieffects = [PullActor (ThrowMod 200 50 1)]  -- 1 step, fast+  , idesc    = "A cargo-hook with a high-tension cord that makes the entangled victim easy to unbalance with a strong pull."+  , ikit     = []+  }+harpoon2 = harpoon+  { iname    = "sharp harpoon"+  , ifreq    = [(COMMON_ITEM, 1), (HARPOON, 2), (S_HARPOON_SHARP, 1)]+  , irarity  = [(10, 5)]+  , idamage  = 8 `d` 1+  , idesc    = "A cord ending in a sharpened cargo-hook that, in addition to entangling the victim, gains purchase biting into the body."+  }+harpoon3 = harpoon+  { iname    = "The Whaling Harpoon"+  , ifreq    = [(TREASURE, 15), (MUSEAL, 50)]+  , iflavour = zipFancy [Red]+  , irarity  = [(8, 4)]+  , idamage  = 7 `d` 1+  , iaspects = SetFlag Unique : iaspects harpoon+  , ieffects = Yell  -- evoke a cry from pain; brutal+               : ieffects harpoon+  , idesc    = "A display piece harking back to the Earth's oceanic tourism heyday. Surprising sharp for its age. The cruel, barbed head lodges in its victim so painfully that the weakest tug of the rope sends the victim flying."+  }+net = ItemKind+  { isymbol  = symbolProjectile+  , iname    = "net"+  , ifreq    = [(COMMON_ITEM, 100), (MERCENARY_AMMO, 25)]+  , iflavour = zipPlain [BrGreen]+  , icount   = 1 `dL` 3+  , irarity  = [(5, 7), (9, 9)]+  , iverbHit = "entangle"+  , iweight  = 1000+  , idamage  = 2 `d` 1+  , iaspects = [AddSkill SkHurtMelee $ -14 * 5]+  , ieffects = [ toOrganBad S_SLOWED (3 + 1 `d` 3)+               , DropItem maxBound 1 CEqp ARMOR_LOOSE+                   -- only one of each kind is dropped, because no rubbish+                   -- in this group and so no risk of exploit+               , SendFlying (ThrowMod 100 50 1) ]  -- 1 step; painful+  , idesc    = "A large synthetic fibre net with weights affixed along the edges. Entangles armor and restricts movement."+  , ikit     = []+  }++-- ** Explosives, with the only effect being @Explode@++fragmentationBomb = ItemKind+  { isymbol  = symbolProjectile+  , iname    = "hand bomb"+      -- improvised bomb filled with iron pellets, nuts, cut nails;+      -- deflagration, not detonation, so large mass and hard container+      -- required not to burn harmlessly; improvised short fuze;+      -- can't be more powerful or would fracture the spaceship's hull+  , ifreq    = [(COMMON_ITEM, 100), (EXPLOSIVE, 300)]+  , iflavour = zipPlain [Red]+  , icount   = 1 `dL` 5  -- many, because not very intricate+  , irarity  = [(5, 10), (10, 5)]+  , iverbHit = "thud"+  , iweight  = 3000  -- low velocity due to weight+  , idamage  = 0  -- heavy and hard, but let's not confuse with blast damage+  , iaspects = [SetFlag Lobable, SetFlag Fragile]+  , ieffects = [ Explode S_FOCUSED_FRAGMENTATION+               , OnSmash (Explode S_VIOLENT_FRAGMENTATION) ]+  , idesc    = "Shards of brittle metal packed around an explosive core."+  , ikit     = []+  }+concussionBomb = fragmentationBomb+  { iname    = "canister"+      -- slightly stabilized liquid explosive in a soft container, hence+      -- no fragmentation, but huge shock wave despite small size and lack+      -- of strong container to build up pressure; indoors help the shock wave;+      -- unstable enough that no fuze required (or simple electric fuse?);+      -- that's the most potent explosive (a detonating one) to be found+      -- and only in small quantities, due to depressurization hazard+  , iflavour = zipPlain [Magenta]+  , iverbHit = "bonk"+  , iweight  = 400+  , iaspects = [ SetFlag Lobable, SetFlag Fragile+               , toVelocity 70 ]  -- flappy and so slow+  , ieffects = [ Explode S_FOCUSED_CONCUSSION+               , OnSmash (Explode S_VIOLENT_CONCUSSION) ]+  , idesc    = "Avoid sudden movements."+  }+-- Not flashbang, because powerful bang without fragmentation is harder+-- to manufacture (requires an oxidizer and steel canister with holes).+-- The bang would also paralyze and/or lower the movement skill+-- (out of balance due to ear trauma).+flashBomb = fragmentationBomb+  { iname    = "powder tube"  -- filled with magnesium flash powder+  , iflavour = zipPlain [BrYellow]  -- avoid @BrWhite@; looks wrong in dark+  , iverbHit = "flash"+  , iweight  = 400+  , iaspects = [ SetFlag Lobable, SetFlag Fragile+               , toVelocity 70 ]  -- bad shape for throwing+  , ieffects = [Explode S_FOCUSED_FLASH, OnSmash (Explode S_VIOLENT_FLASH)]+  , idesc    = "For dramatic entrances and urgent exits."+  }+firecrackerBomb = fragmentationBomb+  { iname = "roll"  -- not fireworks, as they require outdoors+  , iflavour = zipPlain [BrMagenta]+  , irarity  = [(1, 5), (5, 8)]  -- a toy, if harmful+  , iverbHit = "crack"  -- a pun, matches the verb from "ItemKindBlast"+  , iweight  = 1000+  , iaspects = [SetFlag Lobable, SetFlag Fragile]+  , ieffects = [Explode S_FIRECRACKER, OnSmash (Explode S_FIRECRACKER)]+  , idesc    = "String and paper, concealing a deadly surprise."+  }++-- ** Exploding consumables.++-- Not identified, because they are perfect for the id-by-use fun,+-- due to effects. They are fragile and upon hitting the ground explode+-- for effects roughly corresponding to their normal effects.+-- Whether to hit with them or explode them close to the target+-- is intended to be an interesting tactical decision.++-- Flasks are intended to be thrown. They are often not natural: maths, magic,+-- distillery. In fact, they cover all temporary conditions, except those+-- for stats resistance and regeneration. They never heal, directly+-- nor indirectly (regen), so may be thrown without the risk of wasting+-- precious HP.+--+-- There is no flask nor condition that only does Calm or max Calm depletion,+-- because Calm reduced often via combat, etc.++flaskEmpty = flaskTemplate+  { iname    = "empty flask"+  , ifreq    = [(COMMON_ITEM, 100), (S_EMPTY_FLASK, 1)]+  , iflavour = zipGlassPlain [White]+  , icount   = 1+  , irarity  = [(1, 8)]+  , iverbHit = "bang"+  , iweight  = 250+  , iaspects = [SetFlag Lobable, SetFlag Fragile, toVelocity 60]+  , idesc    = "The only redeeming quality of empty flasks is that they can be filled with any liquid."+  }+flaskTemplate = ItemKind+  { isymbol  = symbolFlask+  , iname    = "flask"+  , ifreq    = [(FLASK_UNKNOWN, 1)]+  , iflavour = zipGlassPlain darkCol ++ zipGlassFancy darkCol+               ++ zipLiquid darkCol+  , icount   = 1 `d` 2 + 1 `dL` 3+  , irarity  = [(1, 5), (10, 3)]+  , iverbHit = "splash"+  , iweight  = 500+  , idamage  = 0+  , iaspects = [ PresentAs FLASK_UNKNOWN, SetFlag Lobable, SetFlag Fragile+               , toVelocity 60 ]  -- oily, rather bad grip+  , ieffects = []+  , idesc    = "A flask of oily liquid of a suspect color. Something seems to be moving inside. Double dose causes twice longer effect. Triple dose is not advisable, since the active substance is never without unhealthy side-effects and often dissolved in large volumes of alcohol."+  , ikit     = []+  }+flask1 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100) ]+  , iaspects = ELabel "of strength brew"+               : iaspects flaskTemplate+  , ieffects = [ toOrganGood S_STRENGTHENED (20 + 1 `d` 5)+               , OnSmash (Explode S_DENSE_SHOWER) ]+  }+flask2 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100) ]+  , iaspects = ELabel "of weakness brew"+               : iaspects flaskTemplate+  , ieffects = [ toOrganBad S_WEAKENED (20 + 1 `d` 5)+               , OnSmash (Explode S_SPARSE_SHOWER) ]+  }+flask3 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100), (OIL_SOURCE, 1) ]+  , iaspects = ELabel "of melee protective balm"+               : iaspects flaskTemplate+  , ieffects = [ toOrganGood S_PROTECTED_FROM_MELEE (20 + 1 `d` 5)+               , OnSmash (Explode S_MELEE_PROTECTIVE_BALM) ]+  , idesc    = "A flask of wrestling balm that adheres to the body, but turns into slick oil when hit. Double dose causes twice longer effect."+  }+flask4 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100), (OIL_SOURCE, 1) ]+  , iaspects = ELabel "of ranged protective balm"+               : iaspects flaskTemplate+  , ieffects = [ toOrganGood S_PROTECTED_FROM_RANGED (20 + 1 `d` 5)+               , OnSmash (Explode S_RANGE_PROTECTIVE_BALM) ]+  , idesc    = "A flask of durable body and fabric ointment. Its nanostructure hardens under stress. Double dose causes twice longer effect."+  }+flask5 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100) ]+  , iaspects = ELabel "of fluorescent paint"+               : iaspects flaskTemplate+  , ieffects = [ toOrganBad S_PAINTED (20 + 1 `d` 5)+               , OnSmash (Explode S_PAINT_DROPLET) ]+  }+flask6 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100) ]+  , irarity  = [(1, 1)]  -- not every playthrough needs one+  , iaspects = ELabel "of resolution spirit"+               : iaspects flaskTemplate+  , ieffects = [ toOrganGood S_RESOLUTE (500 + 1 `d` 200)  -- long, for scouting+               , RefillCalm 60  -- not to make it a drawback, via @calmEnough@+               , OnSmash (Explode S_RESOLUTION_DUST) ]+  }+flask7 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100) ]+  , icount   = 1 `d` 2  -- too powerful en masse+  , iaspects = ELabel "of haste brew"+               : iaspects flaskTemplate+  , ieffects = [ toOrganGood S_HASTED (20 + 1 `d` 5)+               , OnSmash (Explode S_HASTE_SPRAY) ]+  }+flask8 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100) ]+  , iaspects = ELabel "of eye drops"+               : iaspects flaskTemplate+  , ieffects = [ toOrganGood S_FAR_SIGHTED (40 + 1 `d` 10)+               , OnSmash (Explode S_EYE_DROP) ]+  }+flask9 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100), (FIRE_FIGHTING_ITEM, 1) ]+  , irarity  = [(10, 2)]  -- not very useful right now+  , iaspects = ELabel "of smelly concoction"+               : iaspects flaskTemplate+  , ieffects = [ toOrganGood S_KEEN_SMELLING (40 + 1 `d` 10)+               , Detect DetectActor 10  -- make it at least slightly useful+               , OnSmash (Explode S_SMELLY_DROPLET) ]+  }+flask10 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100) ]+  , irarity  = [(10, 2)]  -- not very useful right now+  , iaspects = ELabel "of cat tears"+               : iaspects flaskTemplate+  , ieffects = [ toOrganGood S_SHINY_EYED (40 + 1 `d` 10)+               , OnSmash (Explode S_EYE_SHINE) ]+  }+flask11 = flaskTemplate+  { iname    = "bottle"+  , ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100), (ALCOHOL, 100), (FIRE_FIGHTING_ITEM, 2) ]+  , icount   = 1 `d` 2 + 1 `d` 3+  , iaspects = ELabel "of whiskey"+               : iaspects flaskTemplate+  , ieffects = [ toOrganGood S_DRUNK (20 + 1 `d` 5)+               , Burn 10, RefillHP 10, Yell+               , OnSmash (Explode S_WHISKEY_SPRAY) ]+  }+flask12 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100), (WATER_SOURCE, 1) ]+  , iaspects = ELabel "of bait and switch"+               : iaspects flaskTemplate+  , ieffects = [ Summon MOBILE_ANIMAL 1  -- won't work if no Calm; fun exploit+               , Teleport 7  -- escape sometimes worth the summon+               , OnSmash (Summon MOBILE_ANIMAL 1)+               , OnSmash Impress  -- mildly useful when thrown+               , OnSmash (Explode S_WASTE) ]+  }+flask13 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100) ]+  , iaspects = ELabel "of poison"+               : iaspects flaskTemplate+  , ieffects = [ toOrganNoTimer S_POISONED, toOrganNoTimer S_POISONED  -- x2+               , OnSmash (Explode S_POISON_CLOUD) ]+  }+flask14 = flaskTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100) ]+  , iaspects = ELabel "of calamity mixture"+               : iaspects flaskTemplate+  , ieffects = [ toOrganNoTimer S_POISONED+               , toOrganBad S_WEAKENED (20 + 1 `d` 5)+               , toOrganBad S_DEFENSELESS (20 + 1 `d` 5)+               , OnSmash (Explode S_GLASS_HAIL) ]  -- enough glass to cause that+  }+flask15 = flaskTemplate+  { iname    = "cartridge"+  , ifreq    = [ (COMMON_ITEM, 100), (ANY_FLASK, 100), (EXPLOSIVE, 100)+               , (ANY_GLASS, 100), (LIQUID_NITROGEN, 1), (COLD_SOURCE, 1)+               , (FIRE_FIGHTING_ITEM, 40) ]+  , irarity  = [(1, 3)]  -- scavenged from walls+  , iaspects = ELabel "of liquid nitrogen"+               : iaspects flaskTemplate+  , ieffects = [ Burn 1  -- sensory ambiguity between hot and cold+               , toOrganBad S_SLOWED (3 + 1 `d` 3)+               , OnSmash (Explode S_FOCUSED_SLOWNESS_MIST) ]+  }+flask16 = flaskTemplate  -- diluted perfume; almost same effects+  { ifreq    = [ (COMMON_ITEM, 100), (S_ROSE_WATER_FLASK, 1)+               , (ANY_FLASK, 100), (EXPLOSIVE, 100), (ANY_GLASS, 100) ]+  , icount   = 1+  , irarity  = [(1, 3)]  -- mostly obtained through crafting+  , iaspects = ELabel "of rose water"+               : iaspects flaskTemplate+  , ieffects = [ Impress, toOrganGood S_ROSE_SMELLING (100 + 1 `d` 20)+               , OnSmash ApplyPerfume, OnSmash (Explode S_FRAGRANCE) ]+  }+flask17 = flaskTemplate+  { iname    = "galon"+      -- TODO: in the future perhaps have different sizes of flasks;+      -- for now, we freely go from flask to galon and back+  , ifreq    = [ (COMMON_ITEM, 100), (S_WATER_FLASK, 1)+               , (ANY_FLASK, 100), (ANY_GLASS, 100), (WATER_SOURCE, 1)+               , (FIRE_FIGHTING_ITEM, 2) ]+  , icount   = 1+  , irarity  = [(1, 1)]  -- mostly obtained through crafting+  , iaspects = ELabel "of water"+               : iaspects flaskTemplate+  }++-- Vials are often not intended to be thrown. They are usually natural,+-- including natural stat boosts. They also include the only healing+-- consumables in the game, apart of stimpacks and, to a limited extent, fruits.+-- They appear deeper than most flasks. Various configurations of effects.+-- A different class of effects is on scrolls and mechanical items.+-- Some are shared.++potionTemplate = ItemKind+  { isymbol  = symbolPotion+  , iname    = "vial"+  , ifreq    = [(POTION_UNKNOWN, 1)]+  , iflavour = zipLiquid brightCol ++ zipPlain brightCol ++ zipFancy brightCol+  , icount   = 1 `dL` 3+  , irarity  = [(1, 10), (10, 6)]+  , iverbHit = "splash"+  , iweight  = 200+  , idamage  = 0+  , iaspects = [ PresentAs POTION_UNKNOWN, SetFlag Lobable, SetFlag Fragile+               , toVelocity 50 ]  -- oily, small momentum due to small size+  , ieffects = []+  , idesc    = "A vial of bright, frothing concoction. The best medicine that nature has to offer for wounds, ailments and mood swings."+  , ikit     = []+  }+potion1 = potionTemplate+  { ifreq    = [ (COMMON_ITEM, 100), (S_PERFUME_POTION, 1)+               , (ANY_POTION, 100), (ANY_GLASS, 100), (PERFUME, 1) ]+  , icount   = 3 `dL` 1  -- very useful, despite appearances;+                         -- AI heroes can't craft and so die horribly without it+  , iaspects = ELabel "of perfume"+               : iaspects potionTemplate+  , ieffects = [ Impress, toOrganGood S_ROSE_SMELLING (50 + 1 `d` 10)+               , OnSmash ApplyPerfume, OnSmash (Explode S_FRAGRANCE) ]+  }+potion2 = potionTemplate+  { iname    = "the Vial"+  , ifreq    = [(CRAWL_ITEM, 100), (ANY_GLASS, 50)]+  , icount   = 1+  , irarity  = [(5, 6), (10, 2)]+  , iaspects = [ SetFlag Unique, ELabel "of Attraction"+               , SetFlag Precious, SetFlag Lobable, SetFlag Fragile+               , toVelocity 50 ]  -- identified+  , ieffects = [ Dominate+               , toOrganGood S_HASTED (20 + 1 `d` 5)+               , Recharge 20 999+               , OnSmash (Explode S_PHEROMONE)+               , OnSmash (Explode S_HASTE_SPRAY) ]+  , idesc    = "The liquid fizzes with energy."+  }+potion3 = potionTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , ieffects = [ RefillHP 5, DropItem 1 maxBound COrgan S_POISONED+               , OnSmash (Explode S_HEALING_MIST) ]+  }+potion4 = potionTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , irarity  = [(1, 5), (10, 10)]+  , ieffects = [ RefillHP 10+               , DropItem maxBound maxBound COrgan CONDITION+               , OnSmash (Explode S_HEALING_MIST_2) ]+  }+potion5 = potionTemplate+  { iname    = "ampoule"  -- filled with semi-stabilized high explosive liquid+  , ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , icount   = 3 `dL` 1+  , ieffects = [ DropItem 1 maxBound COrgan CONDITION+               , OnSmash (Explode S_VIOLENT_CONCUSSION) ]+      -- not fragmentation nor glass hail, because not enough glass+  }+potion6 = potionTemplate+  -- needs to be common to show at least a portion of effects+  { ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , icount   = 3 `dL` 1  -- always as many as possible on this level+                         -- without giving away potion identity+  , irarity  = [(1, 12)]+  , ieffects = [ OneOf [ RefillHP 10, RefillHP 5, Burn 5+                       , DropItem 1 maxBound COrgan S_POISONED+                       , toOrganGood S_STRENGTHENED (20 + 1 `d` 5) ]+               , OnSmash (OneOf [ Explode S_DENSE_SHOWER+                                , Explode S_SPARSE_SHOWER+                                , Explode S_MELEE_PROTECTIVE_BALM+                                , Explode S_RANGE_PROTECTIVE_BALM+                                , Explode S_DEFENSELESSNESS_RUNOUT ]) ]+  }+potion7 = potionTemplate+  -- needs to be common to show at least a portion of effects+  { ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , icount   = 3 `dL` 1+  , irarity  = [(10, 12)]+  , ieffects = [ Impress+               , OneOf [ RefillHP 20, RefillHP 10, Burn 10+                       , DropItem 1 maxBound COrgan S_POISONED+                       , toOrganGood S_HASTED (20 + 1 `d` 5)+                       , toOrganBad S_IMPATIENT (2 + 1 `d` 2) ]+               , OnSmash (OneOf [ Explode S_HEALING_MIST_2+                                , Explode S_WOUNDING_MIST+                                , Explode S_DISTRESSING_ODOR+                                , Explode $ blastNoStatOf S_IMPATIENT+                                , Explode S_HASTE_SPRAY+                                , Explode S_VIOLENT_SLOWNESS_MIST+                                , Explode S_FRAGRANCE+                                , Explode S_VIOLENT_FLASH ]) ]+  }+potion8 = potionTemplate+  { iname    = "the Vial"+  , ifreq    = [(CRAWL_ITEM, 100), (ANY_GLASS, 50)]+  , icount   = 1+  , irarity  = [(10, 5)]+  , iaspects = [ SetFlag Unique, ELabel "of Love"+               , SetFlag Precious, SetFlag Lobable, SetFlag Fragile+               , toVelocity 50 ]  -- identified+  , ieffects = [ RefillHP 60, RefillCalm (-60)+               , toOrganGood S_ROSE_SMELLING (80 + 1 `d` 20)+               , OnSmash (Explode S_HEALING_MIST_2)+               , OnSmash (Explode S_DISTRESSING_ODOR) ]+  , idesc    = "Perplexing swirls of intense, compelling colour."+  }+potion9 = potionTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , irarity  = [(10, 5)]+  , iaspects = ELabel "of grenadier focus"+               : iaspects potionTemplate+  , ieffects = [ toOrganGood S_MORE_PROJECTING (40 + 1 `d` 10)+               , toOrganBad S_PACIFIED (5 + 1 `d` 3)+                   -- the malus has to be weak, or would be too good+                   -- when thrown at foes+               , OnSmash (Explode $ blastBonusStatOf S_MORE_PROJECTING)+               , OnSmash (Explode $ blastNoStatOf S_PACIFIED) ]+  , idesc    = "Thick, sluggish fluid with violently-bursting bubbles."+  }+potion10 = potionTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , irarity  = [(10, 8)]+  , iaspects = ELabel "of frenzy"+               : iaspects potionTemplate+  , ieffects = [ Yell+               , toOrganGood S_STRENGTHENED (20 + 1 `d` 5)+               , toOrganBad S_RETAINING (5 + 1 `d` 3)+               , toOrganBad S_FRENZIED (40 + 1 `d` 10)+               , OnSmash (Explode S_DENSE_SHOWER)+               , OnSmash (Explode $ blastNoStatOf S_RETAINING)    -- more+               , OnSmash (Explode $ blastNoStatOf S_RETAINING) ]  -- explosion+  }+potion11 = potionTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , irarity  = [(10, 8)]+  , iaspects = ELabel "of panic"+               : iaspects potionTemplate+  , ieffects = [ RefillCalm (-60)+               , toOrganGood S_HASTED (20 + 1 `d` 5)+               , toOrganBad S_WEAKENED (20 + 1 `d` 5)+               , toOrganBad S_WITHHOLDING (10 + 1 `d` 5)+               , OnSmash (Explode S_HASTE_SPRAY)+               , OnSmash (Explode S_SPARSE_SHOWER)+               , OnSmash (Explode $ blastNoStatOf S_WITHHOLDING) ]+  }+potion12 = potionTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , irarity  = [(10, 8)]+  , iaspects = ELabel "of quicksilver"+               : iaspects potionTemplate+  , ieffects = [ toOrganGood S_HASTED (20 + 1 `d` 5)+               , Discharge 3 40+               , toOrganBad S_IMMOBILE (5 + 1 `d` 5)+               , OnSmash (Explode S_HASTE_SPRAY)+               , OnSmash (Explode S_IRON_FILING)+               , OnSmash (Explode $ blastNoStatOf S_IMMOBILE) ]+  }+potion13 = potionTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , irarity  = [(10, 7)]+  , iaspects = ELabel "of slow resistance"+               : iaspects potionTemplate+  , ieffects = [ toOrganNoTimer S_SLOW_RESISTANT+               , OnSmash (Explode S_ANTI_SLOW_MIST) ]+  }+potion14 = potionTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , irarity  = [(10, 7)]+  , iaspects = ELabel "of poison resistance"+               : iaspects potionTemplate+  , ieffects = [ toOrganNoTimer S_POISON_RESISTANT+               , OnSmash (Explode S_ANTIDOTE_MIST) ]+  }+-- The player has full control over throwing the vial at his party,+-- so he can milk the explosion, so it has to be much weaker, so a weak+-- healing effect is enough. OTOH, throwing a harmful flask at many enemies+-- at once is not easy to arrange, so these explosions can stay powerful.+potion15 = potionTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_POTION, 100), (ANY_GLASS, 100)]+  , icount   = 1 `dL` 5+  , irarity  = [(1, 2), (10, 12)]+  , iaspects = ELabel "of regeneration"+               : iaspects potionTemplate+  , ieffects = [ toOrganGood S_ROSE_SMELLING (80 + 1 `d` 20)+               , toOrganNoTimer S_REGENERATING+               , toOrganNoTimer S_REGENERATING  -- x2+               , OnSmash (Explode S_YOUTH_SPRINKLE) ]+  }+potion16 = potionTemplate+  { ifreq    = [(CRAWL_ITEM, 100), (ANY_POTION, 50)]+  , icount   = 1 `dL` 2+  , irarity  = [(1, 1), (10, 6)]+  , iaspects = ELabel "of melee deflection"+               : iaspects potionTemplate+  , ieffects = [ toOrganGood S_MELEE_DEFLECTING 2+               , OnSmash (Explode S_HASTE_SPRAY) ]+                   -- choice of haste instead, e.g., against ranged enemies+  }++-- ** Non-exploding consumables, not specifically designed for throwing++-- Readable or otherwise communicating consumables require high apply skill+-- to be consumed.++scrollTemplate = ItemKind+  { isymbol  = symbolScroll+  , iname    = "chip"+  , ifreq    = [(SCROLL_UNKNOWN, 1)]+  , iflavour = zipFancy stdCol ++ zipPlain stdCol+  , icount   = 1 `dL` 3+  , irarity  = [(1, 12), (10, 6)]+  , iverbHit = "thump"+  , iweight  = 20+  , idamage  = 0+  , iaspects = [ PresentAs SCROLL_UNKNOWN+               , toVelocity 30 ]  -- too small+  , ieffects = []+  , idesc    = "A generic, disposable chip, capable of a one-time holo-display. Some of these also contain a one-time password authorizing a particular spaceship's infrastructure transition. Nobody knows how the infrastructure might respond after so many years."+  , ikit     = []+  }+scroll1 = scrollTemplate+  { iname    = "the Chip"+  , ifreq    = [(CRAWL_ITEM, 100), (ANY_SCROLL, 75)]+  , icount   = 1+  , irarity  = [(5, 10), (10, 7)]  -- mixed blessing so found early for a unique+  , iaspects = [SetFlag Unique, ELabel "of Reckless Beacon"]+               ++ iaspects scrollTemplate+  , ieffects = [Summon HERO 1, Summon MOBILE_ANIMAL (2 + 1 `d` 2)]+  , idesc    = "This industrial, wide-spectrum alarm broadcaster, if over-amped for a single powerful blast, should be able to cut through the interference and reach any lost crew members, giving them enough positional information to locate us."+  }+scroll2 = scrollTemplate+  { ifreq    = [(CRAWL_ITEM, 100), (ANY_SCROLL, 100)]+  , irarity  = [(3, 7)]+  , ieffects = [Ascend True]+  }+scroll3 = scrollTemplate+  -- needs to be common to show at least a portion of effects+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , icount   = 3 `dL` 1+  , irarity  = [(1, 15)]+  , ieffects = [OneOf [ Paralyze 10, InsertMove 30, Recharge 5 999+                      , Detect DetectEmbed 20, Detect DetectHidden 20 ]]+  }+scroll4 = scrollTemplate+  -- needs to be common to show at least a portion of effects+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , icount   = 3 `dL` 1+  , irarity  = [(10, 15)]+  , ieffects = [ Impress+               , OneOf [ Teleport 20, Ascend False, Ascend True+                       , OneOf [Summon HERO 1, Summon MOBILE_ANIMAL $ 1 `d` 2]+                           -- gaining a hero particularly uncommon+                       , Detect DetectLoot 20  -- the most useful of detections+                       , CreateItem Nothing CGround COMMON_ITEM timerNone ] ]+  }+scroll5 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , irarity  = [(1, 6)]  -- powerful, but low counts at the depths it appears on+  , ieffects = [InsertMove $ 20 + 1 `dL` 20]+  }+scroll6 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , ieffects = [PullActor (ThrowMod 800 75 1)]  -- 6 steps, 1.5 turns+  }+scroll7 = scrollTemplate+  { iname    = "the Chip"+  , ifreq    = [(CRAWL_ITEM, 100), (ANY_SCROLL, 75)]+  , icount   = 1+  , irarity  = [(10, 5)]+  , iaspects = [SetFlag Unique, ELabel "of Skeleton Key"]+               ++ iaspects scrollTemplate+  , ieffects = [Summon HERO 1]+  , idesc    = "This is a security lock chip that opens all doors in the area, including the hatch to a nearby closet, resounding from the blows of, as it turns out, one of our lost crew members."+  }+scroll8 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , irarity  = [(10, 12)]  -- powerful, even if not ideal; scares newbies+  , ieffects = [Detect DetectAll 20]+  }+scroll9 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , iaspects = ELabel "of cue interpretation"+               : iaspects scrollTemplate+  , ieffects = [Detect DetectActor 20]+  }+scroll10 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , iaspects = ELabel "of logistics tracking"+               : iaspects scrollTemplate+  , ieffects = [Detect DetectStash 100]+  }+scroll11 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , ieffects = [Discharge 3 40]+  }+scroll12 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , irarity  = [(10, 14)]+  , ieffects = [Recharge 20 999]+  }+scroll13 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , icount   = 3 `dL` 1+  , irarity  = [(1, 18)]  -- uncommon deep down, where all is known+  , iaspects = ELabel "of scientific explanation"+               : iaspects scrollTemplate+  , ieffects = [Identify `AndEffect` RefillCalm 10]+  , idesc    = "The most pressing existential concerns are met with a deeply satisfying scientific answer."+  }+scroll14 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , irarity  = [(10, 20)]  -- at gameover a crucial item may be missing+  , iaspects = ELabel "of molecular reconfiguration"+               : iaspects scrollTemplate+  , ieffects = [PolyItem `AndEffect` Explode S_FIRECRACKER]+  }+scroll15 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , irarity  = [(8, 22)]+  , iaspects = ELabel "of surface reconfiguration"+               : iaspects scrollTemplate+  , ieffects = [RerollItem]+  }+scroll16 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , irarity  = [(8, 18)]+  , iaspects = ELabel "of molecular duplication"+               : iaspects scrollTemplate+  , ieffects = [DupItem]+  }+scrollAd1 = scrollTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_SCROLL, 100)]+  , irarity  = [(1, 3)]+  , iaspects = ELabel "of tourist guide"+               : iaspects scrollTemplate+  , ieffects = [ Impress  -- mostly flavour, but this is useful+               , toOrganGood S_RESOLUTE (500 + 1 `d` 200)+                   -- a drawback (at least initially) due to @calmEnough@+               , Explode S_RHINO_HOLOGRAM+               , Detect DetectLoot 5 ]  -- short so useless most of the time+  , idesc    = "Biodegradable self-powered mini-projector displaying holographic ads and shopping hints."+  }++-- Foods require only minimal apply skill to consume. Many animals can eat them.++rawMeatChunk = ItemKind+  { isymbol  = symbolFood+  , iname    = "raw meat chunk"+  , ifreq    = [ (RAW_MEAT_CHUNK, 100), (COMMON_ITEM, 1)+               , (UNREPORTED_INVENTORY, 1) ]  -- no "fondles a trinket"+  , iflavour = zipPlain [Red]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "slap"+  , iweight  = 1000+  , idamage  = 0+  , iaspects = [toVelocity 50]+  , ieffects = [DropItem maxBound 1 COrgan S_HUNGRY]+  , idesc    = "A scrap of edible animal meat. Not very tasty nor nourishing. Cooking would make it more palatable."+  , ikit     = []+  }+roastedMeatChunk = rawMeatChunk+  { iname    = "roasted meat chunk"+  , ifreq    = [ (ROASTED_MEAT_CHUNK, 100), (COOKED_FOOD, 60)+               , (COMMON_ITEM, 1) ]+  , iflavour = zipPlain [Brown]+  , ieffects = [DropItem maxBound 3 COrgan S_HUNGRY]+  , idesc    = "Delicious and filling chunk of meat. The thermal processing released flavour and made it easier to digest."+  }+ediblePlantTemplate = ItemKind+  { isymbol  = symbolFood+  , iname    = "edible plant"+  , ifreq    = [(EDIBLE_PLANT_UNKNOWN, 1)]+  , iflavour = zipFancy stdCol+  , icount   = 1 `dL` 5+  , irarity  = [(1, 3), (10, 2)]  -- weak, apart of hunger removal+  , iverbHit = "thump"+  , iweight  = 300+  , idamage  = 0+  , iaspects = [ PresentAs EDIBLE_PLANT_UNKNOWN+               , toVelocity 30 ]  -- low density, often falling apart+  , ieffects = []+  , idesc    = "Withered but fragrant bits of a colorful plant. Taste tolerably. Doesn't break down that easily in its raw form, without cooking. Only eating may reveal the full effects."+  , ikit     = []+  }+ediblePlant1 = ediblePlantTemplate+  { iname    = "enhanced berry"+  , ifreq    = [(S_ENCHANCED_BERRY, 1), (COMMON_ITEM, 100), (EDIBLE_PLANT, 100)]+  , ieffects = [ RefillHP 1, toOrganBad S_IMMOBILE (5 + 1 `d` 5)+               , DropItem maxBound 1 COrgan S_HUNGRY ]+  }+ediblePlant2 = ediblePlantTemplate+  { iname    = "frayed fungus"+  , ifreq    = [(S_FRAYED_FUNGUS, 1), (COMMON_ITEM, 100), (EDIBLE_PLANT, 100)]+  , ieffects = [toOrganNoTimer S_POISONED]+  }+ediblePlant3 = ediblePlantTemplate+  { iname    = "thick leaf"+  , ifreq    = [(S_THIC_LEAF, 1), (COMMON_ITEM, 100), (EDIBLE_PLANT, 100)]+  , ieffects = [ DropItem 1 maxBound COrgan S_POISONED+               , DropItem maxBound 2 COrgan S_HUNGRY ]+  }+ediblePlant4 = ediblePlantTemplate+  { iname    = "reconfigured fruit"+  , ifreq    = [ (S_RECONFIGURED_FRUIT, 1), (COMMON_ITEM, 100)+               , (EDIBLE_PLANT, 100) ]+  , ieffects = [ toOrganBad S_BLIND (10 + 1 `d` 10)+               , DropItem maxBound 3 COrgan S_HUNGRY ]+  }+ediblePlant5 = ediblePlantTemplate+  { iname    = "fragrant herb"+  , ifreq    = [(S_FRAGRANT_HERB, 1), (COMMON_ITEM, 100), (EDIBLE_PLANT, 100)]+  , icount   = 1 `dL` 9+  , irarity  = [(1, 3)]  -- powerful; many copies+  , iaspects = ELabel "of lethargy"+               : iaspects ediblePlantTemplate+  , ieffects = [ toOrganBad S_SLOWED (20 + 1 `d` 5)+               , toOrganNoTimer S_REGENERATING+               , toOrganNoTimer S_REGENERATING  -- x2+               , RefillCalm 5 ]  -- too many effects to also add hunger removal+  }+ediblePlant6 = ediblePlantTemplate+  { iname    = "dull flower"+  , ifreq    = [ (S_DULL_FLOWER, 1), (COMMON_ITEM, 100), (EDIBLE_PLANT, 100)+               , (PERFUME, 1) ]+  , ieffects = [ PutToSleep+               , DropItem maxBound 1 COrgan S_HUNGRY ]+  }+ediblePlant7 = ediblePlantTemplate+  { iname    = "spicy bark"+  , ifreq    = [(S_SPICY_BARK, 1), (COMMON_ITEM, 100), (EDIBLE_PLANT, 100)]+  , ieffects = [ InsertMove 20, toOrganBad S_FRENZIED (40 + 1 `d` 10)+               , DropItem maxBound 1 COrgan S_HUNGRY ]+  }+ediblePlant8 = ediblePlantTemplate+  { iname    = "pumpkin"+  , ifreq    = [(S_PUMPKIN, 1), (COMMON_ITEM, 100), (EDIBLE_PLANT, 100)]+  , irarity  = [(1, 2), (10, 4)]  -- solves the hunger problem, but not too soon+  , iweight  = 3000+  , idamage  = 1 `d` 1+  , ieffects = [DropItem maxBound 1 COrgan S_HUNGRY]+  }+cookedPlantTemplate = ediblePlantTemplate+  { iname    = "cooked plant"+  , ifreq    = [(COOKED_PLANT_UNKNOWN, 1)]+  , iflavour = zipPlain stdCol+  , irarity  = [(1, 1)]+  , iaspects = [ PresentAs COOKED_PLANT_UNKNOWN+               , toVelocity 20 ]  -- low density, often falling apart+  , idesc    = "Withered but fragrant bits of a colorful plant. Taste blandly, but break down easily, releasing all nutrients. Only eating may reveal the full effects."+  }+cookedPlant1 = cookedPlantTemplate+  { iname    = "cooked berry"+  , ifreq    = [(S_COOKED_BERRY, 1), (COMMON_ITEM, 1), (COOKED_PLANT, 100)]+  , ieffects = [ RefillHP 1, toOrganBad S_IMMOBILE (5 + 1 `d` 5)+               , DropItem maxBound 2 COrgan S_HUNGRY ]+  }+cookedPlant2 = cookedPlantTemplate+  { iname    = "cooked fungus"+  , ifreq    = [(S_COOKED_FUNGUS, 1), (COMMON_ITEM, 1), (COOKED_PLANT, 100)]+  , ieffects = ieffects ediblePlant2+  }+cookedPlant3 = cookedPlantTemplate+  { iname    = "cooked leaf"+  , ifreq    = [ (S_COOKED_LEAF, 1)+               , (COMMON_ITEM, 1), (COOKED_PLANT, 100), (COOKED_FOOD, 10) ]+  , ieffects = [ DropItem 1 maxBound COrgan S_POISONED+               , DropItem maxBound 3 COrgan S_HUNGRY ]+  }+cookedPlant4 = cookedPlantTemplate+  { iname    = "cooked fruit"+  , ifreq    = [ (S_COOKED_FRUIT, 1)+               , (COMMON_ITEM, 1), (COOKED_PLANT, 100) ]+  , ieffects = [ toOrganBad S_BLIND (10 + 1 `d` 10)+               , DropItem maxBound 4 COrgan S_HUNGRY ]+  }+cookedPlant5 = cookedPlantTemplate+  { iname    = "cooked herb"+  , ifreq    = [ (S_COOKED_HERB, 1)+               , (COMMON_ITEM, 1), (COOKED_PLANT, 100) ]+  , icount   = 1 `dL` 9+  , iaspects = ELabel "of lethargy"+               : iaspects cookedPlantTemplate+  , ieffects = ieffects ediblePlant5+  }+cookedPlant6 = cookedPlantTemplate+  { iname    = "cooked flower"+  , ifreq    = [ (S_COOKED_FLOWER, 1)+               , (COMMON_ITEM, 1), (COOKED_PLANT, 100), (COOKED_FOOD, 10) ]+  , ieffects = [ PutToSleep+               , DropItem maxBound 2 COrgan S_HUNGRY ]+  }+cookedPlant7 = cookedPlantTemplate+  { iname    = "cooked bark"+  , ifreq    = [ (S_COOKED_BARK, 1)+               , (COMMON_ITEM, 1), (COOKED_PLANT, 100), (COOKED_FOOD, 10) ]+  , ieffects = [ InsertMove 20, toOrganBad S_FRENZIED (40 + 1 `d` 10)+               , DropItem maxBound 2 COrgan S_HUNGRY ]+  }+cookedPlant8 = cookedPlantTemplate+  { iname    = "cooked pumpkin"+  , ifreq    = [ (S_COOKED_PUMPKIN, 1)+               , (COMMON_ITEM, 1), (COOKED_PLANT, 100), (COOKED_FOOD, 10) ]+  , iweight  = 3000+  , idamage  = 1 `d` 1+  , ieffects = [DropItem maxBound 5 COrgan S_HUNGRY]+  }++-- ** Lights and related++torchMsg :: Effect+torchMsg = VerbMsgFail "feel the torch fracture" "."+torchDestruct :: Effect+torchDestruct =+  OnUser $ OneOf $+    DestroyItem 1 1 CEqp S_WOODEN_TORCH+    `AndEffect`+    CreateItem Nothing CStash S_RAG_TANGLE timerNone+      -- staff broken, cord not usable (if a cord was used for crafting);+      -- otherwise rag + staff would produce a cord (bonding tool)+    : DestroyItem 1 1 CEqp S_WOODEN_TORCH+      `AndEffect`+      CreateItem Nothing CStash S_DOUSED_WOODEN_TORCH timerNone+    : replicate 6 torchMsg  -- twice more durable than gardening tools+light1 = ItemKind+  { isymbol  = symbolLight+  , iname    = "torch"+  , ifreq    = [ (COMMON_ITEM, 50), (LIGHT_ATTENUATOR, 70), (WEAK_ARROW, 300)+               , (FIRE_SOURCE, 1), (S_WOODEN_TORCH, 1) ]+  , iflavour = zipPlain [Brown]+  , icount   = 1+  , irarity  = [(3 * 10/15, 15), (4 * 10/15, 1)]+                 -- crafted, so rare; later taken from aliens+  , iverbHit = "scorch"+  , iweight  = 1000+  , idamage  = 1 `d` 1  -- strong missile, but betrays the flinger+  , iaspects = [ AddSkill SkShine 3, AddSkill SkSight (-2)+                   -- not only flashes, but also sparks,+                   -- so unused by AI due to the mixed blessing+               , SetFlag Durable, SetFlag Lobable+               , SetFlag Meleeable, EqpSlot EqpSlotShine ]+                   -- partially durable; reusable flare;+                   -- the staff culled when crafting, so no velocity malus+  , ieffects = [Burn 2, torchDestruct]  -- no timeout, but destructs+  , idesc    = "A puttering torch improvised with rags on a staff, soaked in any lubricant or oil or resin or tar that could be scavenged in a hurry."+  , ikit     = []+  }+lightDoused1 = light1+  { iname    = "doused torch"+  , ifreq    = [(S_DOUSED_WOODEN_TORCH, 1) ]+  , iverbHit = "prod"+  , iaspects = [SetFlag Lobable]  -- not durable, so not OP missile+  , ieffects = []+  , idesc    = "A yet unlit torch improvised with rags on a staff, soaked in any lubricant or oil or resin or tar that could be scavenged in a hurry."+  }+light2 = ItemKind+  { isymbol  = symbolLight+  , iname    = "oil lamp"+  , ifreq    = [ (COMMON_ITEM, 50), (LIGHT_ATTENUATOR, 70)+               , (S_OIL_LAMP, 1) ]+  , iflavour = zipPlain [BrYellow]+  , icount   = 1+  , irarity  = [(5, 2)]+  , iverbHit = "burn"+  , iweight  = 1600+  , idamage  = 1 `d` 1+  , iaspects = [ AddSkill SkShine 3, AddSkill SkSight (-1)+               , SetFlag Lobable, SetFlag Fragile, SetFlag Equipable+               , EqpSlot EqpSlotShine ]+  , ieffects = [ Explode S_FOCUSED_BURNING_OIL_2+               , OnSmash (Explode S_VIOLENT_BURNING_OIL_2) ]+  , idesc    = "A restaurant table glass lamp filled with plant oil feeding a slender wick. Or a makeshift caricature thereof."+  , ikit     = []+  }+lightDoused2 = light2+  { iname    = "doused oil lamp"+  , ifreq    = [(S_DOUSED_OIL_LAMP, 1)]+  , iverbHit = "bonk"+  , iaspects = [SetFlag Lobable, SetFlag Fragile]+  , ieffects = []+  , idesc    = "An unlit restaurant table glass lamp filled with plant oil feeding a slender wick. Or a makeshift caricature thereof."+  }+light3 = ItemKind+  { isymbol  = symbolLight+  , iname    = "brass lantern"+  , ifreq    = [(COMMON_ITEM, 100), (MUSEAL, 100), (LIGHT_ATTENUATOR, 5)]+  , iflavour = zipPlain [Red]+  , icount   = 1+  , irarity  = [(6, 1), (10, 4)]+  , iverbHit = "burn"+  , iweight  = 3000+  , idamage  = 2 `d` 1+  , iaspects = [ AddSkill SkShine 4, AddSkill SkSight (-1)+               , SetFlag Lobable, SetFlag Fragile, SetFlag Equipable+               , EqpSlot EqpSlotShine ]+  , ieffects = [ Explode S_FOCUSED_BURNING_OIL_4+               , OnSmash (Explode S_VIOLENT_BURNING_OIL_4) ]+  , idesc    = "Very old, very bright and very heavy lantern made of hand-polished brass."+  , ikit     = []+  }+blanket = ItemKind+  { isymbol  = symbolLight+  , iname    = "mineral fibre blanket"+  , ifreq    = [ (COMMON_ITEM, 100), (LIGHT_ATTENUATOR, 20), (THICK_CLOTH, 1)+               , (FIREPROOF_CLOTH, 1), (FIRE_FIGHTING_ITEM, 100)+               , (SHARPENING_TOOL, 1) ]+  , iflavour = zipPlain [Magenta]+  , icount   = 1+  , irarity  = [(1, 1)]  -- scavenged from walls+  , iverbHit = "swoosh"+  , iweight  = 1000+  , idamage  = 0+  , iaspects = [ AddSkill SkShine (-10)+               , AddSkill SkArmorMelee 3, AddSkill SkMaxCalm 5+               , SetFlag Lobable, SetFlag Equipable+               , EqpSlot EqpSlotArmorMelee ]+                   -- not Fragile; reusable douse implement;+                   -- douses torch, lamp and lantern in one action,+                   -- both in equipment and when thrown at the floor+  , ieffects = []+  , idesc    = "Flame-retardant synthetic fibres. The strong cloth polishes metals really well when soaked, aiding in the final stages of blade sharpening."+  , ikit     = []+  }++-- ** Tools used for terrain transformation and crafting, not for wearing++chisel = ItemKind  -- ignored by AI, but that's fine, others suffice+  { isymbol  = symbolTool+  , iname    = "chisel"+  , ifreq    = [(CRAWL_ITEM, 10), (BREACHING_TOOL, 1), (TOOL_ONLY, 1)]+  , iflavour = zipPlain [Cyan]+  , icount   = 1+  , irarity  = [(1, 110)]+  , iverbHit = "dismantle"+  , iweight  = 500+  , idamage  = 0  -- not a missile to avoid wasting, before a workshop found+  , iaspects = []  -- lost after one use; a consumable+  , ieffects = []+  , idesc    = "It is a breaching tool that can be used for crafting and terrain modification. There are no junk items in space."  -- TODO: https://en.wikipedia.org/wiki/Chisel+                   -- also say light and cheap, but not durable; one time use+  , ikit     = [(SHARPENING_TOOL, CGround), (WIRECUTTING_TOOL, CGround)]+  }+hacksaw = chisel+  { iname    = "hacksaw"+  , idesc    = "It is a breaching tool that can be used for crafting and terrain modification. There are no junk items in space."  -- TODO: https://en.wikipedia.org/wiki/Hacksaw+  }+adjustableSpanner = chisel+  { iname    = "adjustable spanner"+  , idesc    = "It is a breaching tool that can be used for crafting and terrain modification. There are no junk items in space."  -- TODO: https://en.wikipedia.org/wiki/Adjustable_spanner+  , ikit     = [ (SHARPENING_TOOL, CGround), (WIRECUTTING_TOOL, CGround)+               , (BONDING_TOOL, CGround) ]+  }+steelFile = chisel+  { iname    = "steel file"+  , ifreq    = [ (CRAWL_ITEM, 20), (BREACHING_TOOL, 1), (SHARPENING_TOOL, 1)+               , (TOOL_ONLY, 1) ]+  , iflavour = zipPlain [Red]  -- double purpose, saves one tool sometimes+  , iverbHit = "grate"+  , idesc    = "It is a breaching and sharpening tool that can be used for crafting and terrain modification. There are no junk items in space."  -- TODO: https://en.wikipedia.org/wiki/File_(tool)+  , ikit     = []+  }+honingSteel = chisel+  { iname    = "honing steel"+  , ifreq    = [(CRAWL_ITEM, 10), (SHARPENING_TOOL, 1), (TOOL_ONLY, 1)]+  , iflavour = zipFancy [Blue]+  , iverbHit = "hone"+  , idesc    = "Originally used for realigning and sharpening dulled edges of kitchen knives in the local restaurants. Now it turns utensils into weapons."+  , ikit     = [(WIRECUTTING_TOOL, CGround), (BONDING_TOOL, CGround)]+  }+whetstone = honingSteel+  { iname    = "whetstone"+  , iverbHit = "rub"+  , idesc    = "A portable sharpening stone that can transforms a dull piece of scrap into a keen and true blade."+  }+diagonalPliers = chisel+  { iname    = "pair"+  , ifreq    = [(CRAWL_ITEM, 10), (WIRECUTTING_TOOL, 1), (TOOL_ONLY, 1)]+  , iflavour = zipPlain [Brown]+  , iverbHit = "cut"+  , iaspects = [ELabel "of diagonal pliers"]+  , idesc    = "It is a wirecutting tool that can be used for crafting and terrain modification. There are no junk items in space."  -- TODO: https://en.wikipedia.org/wiki/Diagonal_pliers+  , ikit     = [(BREACHING_TOOL, CGround)]+  }+snips = diagonalPliers+  { iname    = "pair"+  , iaspects = [ELabel "of snips"]+  , idesc    = "It is a wirecutting tool that can be used for crafting and terrain modification. There are no junk items in space."  -- TODO: https://en.wikipedia.org/wiki/Snips+  }+loppers = diagonalPliers+  { iname    = "pair"+  , iaspects = [ELabel "of loppers"]+  , idesc    = "It is a wirecutting tool that can be used for crafting and terrain modification. There are no junk items in space."  -- TODO: https://en.wikipedia.org/wiki/Loppers+  , ikit     = [(BREACHING_TOOL, CGround), (SHARPENING_TOOL, CGround)]+  }+boltCutter = loppers+  { iname    = "bolt cutter"+  , iaspects = []+  , idesc    = "It is a wirecutting tool that can be used for crafting and terrain modification. There are no junk items in space."  -- TODO: https://en.wikipedia.org/wiki/Bolt_cutter+  }+solderingIron = chisel+  { iname    = "soldering iron"+  , ifreq    = [(CRAWL_ITEM, 5), (BONDING_TOOL, 20), (TOOL_ONLY, 1)]+  , iflavour = zipPlain [White]+  , iverbHit = "soldier"+  , idesc    = "It is a bonding tool that can be used for crafting and terrain modification. There are no junk items in space."  -- TODO: wikipedia+  , ikit     = []+  }+duckTape = solderingIron+  { iname    = "duck tape"+  , ifreq    = [(CRAWL_ITEM, 3), (BONDING_TOOL, 40), (TOOL_ONLY, 1)]+  , icount   = 1 `d` 4+  , iverbHit = "catch"+  , idesc    = "It is a bonding tool that can be used for crafting and terrain modification. There are no junk items in space."  -- TODO: https://en.wikipedia.org/wiki/Duct_tape+  }+thickCord = solderingIron+  { iname    = "thick cord"+  , ifreq    = [ (CRAWL_ITEM, 7), (BONDING_TOOL, 30)+               , (CLOTH_RAG, 1), (THICK_CLOTH, 1), (TOOL_ONLY, 1) ]+  , iverbHit = "tie"+  , idesc    = "It is a bonding tool and it soaks fluids."  -- TODO+  }++-- ** Periodic jewelry++-- This looks like a necklace, but is not periodic. Instead, it auto-activates+-- when under melee attack.+gorget = necklaceTemplate+  { iname    = "Old Gorget"+  , ifreq    = [(TREASURE, 50), (MUSEAL, 100)]+  , iflavour = zipFancy [BrCyan]  -- looks exactly the same as one of necklaces,+                                  -- but it's OK, it's an artifact+  , iaspects = [ SetFlag Unique+               , Timeout $ 7 - 1 `dL` 4+                   -- the dL dice need to be in negative positions+                   -- for negative stats, such as @Timeout@, so that+                   -- the @RerollItem@ effect makes the item better, not worse+               , AddSkill SkArmorMelee 3, AddSkill SkHearing 3+               , SetFlag UnderMelee, SetFlag Durable ]+               ++ delete (SetFlag Periodic) iaspects_necklaceTemplate+  , ieffects = [RefillCalm 15]+  , idesc    = "Worn, cold, large brass medallion on a chain. Unlikely to offer much protection as an armor piece, but when courage is hardest to sustain, the old engraving reassures its wearer."+  }+-- Morally these are the aspects, but we also need to add a fake @Timeout@,+-- to let clients know that the not identified item is periodic jewelry.+iaspects_necklaceTemplate :: [Aspect]+iaspects_necklaceTemplate =+  [ PresentAs NECKLACE_UNKNOWN+  , SetFlag Periodic, SetFlag Precious, SetFlag Equipable+  , toVelocity 50 ]  -- not dense enough+-- Not identified, because id by use, e.g., via periodic activations. Fun.+necklaceTemplate = ItemKind+  { isymbol  = symbolNecklace+  , iname    = "necklace"+  , ifreq    = [(NECKLACE_UNKNOWN, 1)]+  , iflavour = zipFancy stdCol ++ zipPlain brightCol+  , icount   = 1+  , irarity  = [(4, 3), (10, 4)]+  , iverbHit = "whip"+  , iweight  = 100+  , idamage  = 0+  , iaspects = Timeout 1000000+                 -- fake, needed to display "charging"; the timeout itself+                 -- won't be displayed thanks to periodic; as a side-effect,+                 -- it can't be activated until identified, which is better+                 -- than letting the player try to activate before the real+                 -- cooldown is over and waste turn+               : iaspects_necklaceTemplate+  , ieffects = []+  , idesc    = "Tingling, rattling chain of flat encrusted links. Eccentric millionaires are known to hide their highly personalized body augmentation packs in bulky jewelry pieces such as these."+  , ikit     = []+  }+necklace1 = necklaceTemplate+  { iname    = "the Necklace"+  , ifreq    = [(CRAWL_ITEM, 50), (ANY_JEWELRY, 25)]+  , irarity  = [(3 * 10/15, 0), (4 * 10/15, 1), (10, 5)]+                 -- prevents camping on lvl 3+  , iaspects = [ SetFlag Unique, ELabel "of Spur Life"+               , Timeout $ (4 - 1 `dL` 3) * 10+                   -- priceless, so worth the long wait and the malus+               , AddSkill SkArmorMelee (-30)+               , SetFlag Durable ]+               ++ iaspects_necklaceTemplate+  , ieffects = [ RefillCalm (-5)+               , When (TriggeredBy ActivationPeriodic) $ RefillHP 1 ]+  , idesc    = "This awkward chain, when worn on bare skin, frequently emits mild but highly annoying electric shocks, which apparently stimulate tissue regeneration even in distant parts of the body. A part of the surprising effectiveness of this unique artifact may stem from the desperation of the patients to be quickly healed enough to take it off."+  }+-- no necklace2 of Live Bait, wasteContainer too similar+necklace3 = necklaceTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100)]+  , iaspects = [ ELabel "of fearful listening"+               , Timeout 40+                   -- has to be larger than Calm drain or item not removable;+                   -- equal is not enough if enemies drained Calm already+               , AddSkill SkHearing 6 ]+               ++ iaspects_necklaceTemplate+  , ieffects = [ Detect DetectActor 20  -- can be applied; destroys the item+               , When (TriggeredBy ActivationPeriodic) $ RefillCalm (-30) ]+  }+necklace4 = necklaceTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100)]+  , iaspects = [ ELabel "of escape"+               , Timeout $ (7 - 1 `dL` 5) * 10 ]+               ++ iaspects_necklaceTemplate+  , ieffects = [ Teleport $ 14 + 3 `d` 3  -- can be applied; destroys the item+               , Detect DetectExit 20+               , Yell ]  -- drawback when used for quick exploring+  , idesc    = "A supple chain that slips through your fingers."+  }+necklace5 = necklaceTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100)]+  , iaspects = [ ELabel "of greed"+               , Timeout (8 + 1 `d` 3) ]+               ++ iaspects_necklaceTemplate+  , ieffects = [ Detect DetectStash 100+               , toOrganBad S_PARSIMONIOUS (5 + 1 `d` 3)  -- hard to flee+               , When (TriggeredBy ActivationPeriodic) $ Teleport 40 ]  -- risky+  }+necklace6 = necklaceTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100)]+  , iaspects = Timeout ((3 + 1 `d` 3 - 1 `dL` 3) * 2)+               : iaspects_necklaceTemplate  -- OP if Durable; free blink+  , ieffects = [Teleport $ 3 `d` 2]+  }+necklace7 = necklaceTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100)]+  , iaspects = Timeout ((1 `d` 3) * 2)+               : iaspects_necklaceTemplate+  , ieffects = [PushActor (ThrowMod 100 50 1)]  -- 1 step, slow+                  -- the @50@ is only for the case of very light actor, etc.+  }+necklace8 = necklaceTemplate+  { iname    = "the Necklace"+  , ifreq    = [(CRAWL_ITEM, 50), (ANY_JEWELRY, 50)]+  , irarity  = [(10, 5)]  -- powerful and determines tactics for one actor+  , iaspects = [ SetFlag Unique, ELabel "of Overdrive"+               , Timeout 10+               , AddSkill SkMaxHP 10  -- good effects vanish when taken off+               , AddSkill SkSpeed 10+               , SetFlag Durable ]+               ++ iaspects_necklaceTemplate+  , ieffects = [ RefillCalm (-2)  -- don't spam+               , Discharge 5 80 ]+                   -- discharged again soon after it ends+                 -- Lasting effect lessens temptation to frequently take off+                 -- when engaging in melee, which would lead to micromanagement.+                 -- Quite OOP if worn with the right set of other items, anyway.+  , idesc    = "This whirring augmentation pack stimulates its host beyond any medically advisable or, surely, even legally admissible levels. It can be only speculated what kind of activity it was designed for, but clearly the steady handling of melee weapons was not one of them."+  }+necklace9 = necklaceTemplate+  { iname    = "coil"+  , ifreq    = [ (COMMON_ITEM, 20)  -- crafted, so can be rare+               , (S_REFRIGERATION_COIL, 1), (ANY_JEWELRY, 100)+               , (COLD_SOURCE, 1) ]+  , iaspects = ELabel "of superconducting refrigeration"+               : Timeout ((1 + 1 `d` 3) * 5)+               : delete (SetFlag Precious) iaspects_necklaceTemplate+  , ieffects = [Explode S_CURRENT_DISCHARGE]+  }+necklace10 = necklaceTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100), (PERFUME, 1)]+  , iaspects = Timeout ((1 + 1 `d` 3) * 5)+               : iaspects_necklaceTemplate+  , ieffects = [Explode S_FRAGRANCE]+  }+motionScanner = necklaceTemplate+  { iname    = "handheld sonar"+  , ifreq    = [(COMMON_ITEM, 100), (ADD_NOCTO_1, 20)]+  , irarity  = [(5, 3)]+  , iverbHit = "ping"+  , iweight  = 300  -- almost gives it away+  , iaspects = [ Timeout $ 4 + 1 `dL` 6+                   -- positive dL dice, since the periodic effect is detrimental+               , AddSkill SkNocto 1+               , AddSkill SkArmorMelee $ (-4 + 1 `dL` 3) * 5+               , EqpSlot EqpSlotMiscBonus ]+               ++ iaspects_necklaceTemplate+  , ieffects = [Explode S_PING_PLASH]+  , idesc    = "Portable underwater echolocator overdriven to scan dark corridors at the cost of emitting occasional loud pings and flashes. Having to track the display hanging from the neck strap is distracting, as well."+  }++-- ** Non-periodic jewelry++imageItensifier = ItemKind+  { isymbol  = symbolRing+  , iname    = "noctovisor"+  , ifreq    = [(TREASURE, 100), (ADD_NOCTO_1, 80), (MUSEAL, 100)]+  , iflavour = zipFancy [BrGreen]+  , icount   = 1+  , irarity  = [(5, 2)]+  , iverbHit = "rattle"+  , iweight  = 700+  , idamage  = 0+  , iaspects = [ AddSkill SkNocto 1, AddSkill SkSight (-1)+               , AddSkill SkArmorMelee $ (-6 + 1 `dL` 3) * 5+               , SetFlag Precious, SetFlag Equipable+               , EqpSlot EqpSlotMiscBonus ]+  , ieffects = []+  , idesc    = "Sturdy antique night vision goggles of unknown origin."+  , ikit     = []+  }+sightSharpening = ringTemplate  -- small and round, so mistaken for a ring+  { iname    = "Autozoom Contact Lens"+  , ifreq    = [(TREASURE, 100), (ADD_SIGHT, 1)]+      -- it's has to be very rare, because it's powerful and not unique,+      -- and also because it looks exactly as one of necklaces, so it would+      -- be misleading when seen on the map+  , irarity  = [(7, 1), (10, 10)]  -- low @ifreq@+  , iweight  = 50  -- heavier that it looks, due to glass+  , iaspects = [ AddSkill SkSight $ 1 + 1 `dL` 2+               , AddSkill SkHurtMelee $ (1 `d` 3) * 3+               , EqpSlot EqpSlotSight ]+               ++ iaspects ringTemplate+  , idesc    = "Zooms on any movement, distant or close. Requires some getting used to. Never needs to be taken off."+  }+-- Don't add standard effects to rings, because they go in and out+-- of eqp and so activating them would require UI tedium: looking for+-- them in eqp and stash or even activating a wrong item by mistake.+--+-- By general mechanisms, due to not having effects that could identify+-- them by observing the effect, rings are identified on pickup.+-- That's unlike necklaces, which provide the fun of id-by-use, because they+-- have effects and when the effects are triggered, they get identified.+ringTemplate = ItemKind+  { isymbol  = symbolRing+  , iname    = "ring"+  , ifreq    = [(RING_UNKNOWN, 1)]+  , iflavour = zipPlain stdCol ++ zipFancy darkCol+  , icount   = 1+  , irarity  = [(8, 7)]+  , iverbHit = "knock"+  , iweight  = 15+  , idamage  = 0+  , iaspects = [PresentAs RING_UNKNOWN, SetFlag Precious, SetFlag Equipable]+  , ieffects = []+  , idesc    = "A sturdy ring with a softly shining eye. If it contains a body booster unit, beware of the side-effects."+  , ikit     = []+  }+ring1 = ringTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100)]+  , irarity  = [(5, 4)]+  , iaspects = [ AddSkill SkSpeed $ 1 `dL` 2+               , AddSkill SkMaxHP (-20)+               , EqpSlot EqpSlotSpeed ]+               ++ iaspects ringTemplate+  }+ring2 = ringTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100)]+  , irarity  = [(8, 4)]+  , iaspects = [ AddSkill SkSpeed $ 1 + 1 `dL` 3+               , AddSkill SkArmorMelee (-40)+               , EqpSlot EqpSlotSpeed ]+               ++ iaspects ringTemplate+  }+ring3 = ringTemplate+  { iname    = "the Ring"+  , ifreq    = [(CRAWL_ITEM, 50), (ANY_JEWELRY, 20)]+  , irarity  = [(10, 7)]+  , iaspects = [ SetFlag Unique, ELabel "of Rush"+               , AddSkill SkSpeed $ (1 + 1 `dL` 2) * 2+               , AddSkill SkMaxHP (-20)+               , AddSkill SkArmorMelee (-20)+               , SetFlag Durable, EqpSlot EqpSlotSpeed ]+               ++ iaspects ringTemplate+  , idesc    = "The creator of this dangerous artifact didn't find time to document its operation. And now it's too late."+  }+ring4 = ringTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100)]+  , irarity  = [(5, 5)]+  , iaspects = [ AddSkill SkHurtMelee $ (2 + 1 `d` 3 + (1 `dL` 2) * 2 ) * 3+               , AddSkill SkMaxHP (-10)+               , EqpSlot EqpSlotHurtMelee ]+               ++ iaspects ringTemplate+  }+ring5 = ringTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100)]+  , iaspects = [ AddSkill SkHurtMelee $ (4 + 1 `d` 3 + (1 `dL` 2) * 2 ) * 3+               , AddSkill SkArmorMelee (-20)+               , EqpSlot EqpSlotHurtMelee ]+               ++ iaspects ringTemplate+  }+ring6 = ringTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100)]+  , irarity  = [(6, 10)]  -- needed, e.g, to buff dominated minions+  , iaspects = [ AddSkill SkMaxHP $ 5 + (1 `d` 2 + 1 `dL` 2) * 5+               , AddSkill SkMaxCalm $ -30 + (1 `dL` 3) * 5+               , EqpSlot EqpSlotMaxHP ]+               ++ iaspects ringTemplate+  }+ring7 = ringTemplate+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100), (MUSEAL, 100)]+  , irarity  = [(5, 1), (10, 7)]  -- needed after other items drop Calm+  , iaspects = [ AddSkill SkMaxCalm $ 30 + (1 `dL` 4) * 5+               , AddSkill SkHearing 6+               , EqpSlot EqpSlotMiscBonus ]+               ++ iaspects ringTemplate+  , idesc    = "Cold, solid to the touch, perfectly round, engraved with solemn, strangely comforting, worn out words."+  }+ring8 = ringTemplate  -- weak skill per eqp slot, so can be without drawbacks+  { ifreq    = [(COMMON_ITEM, 100), (ANY_JEWELRY, 100), (MUSEAL, 50)]+  , irarity  = [(3, 3)]+  , iaspects = [ AddSkill SkShine 1+               , EqpSlot EqpSlotShine ]+               ++ iaspects ringTemplate+  , idesc    = "A sturdy ring with a large, shining stone."+  }+ring9 = ringTemplate+  { ifreq    = [(COMMON_ITEM, 10), (RING_OF_OPPORTUNITY_SNIPER, 1)]+  , iaspects = [ ELabel "of opportunity sniper"+               , AddSkill SkProject 8+               , EqpSlot EqpSlotProject ]+               ++ iaspects ringTemplate+  , idesc    = "This mil-grade communication equipment feeds the aggregated enemy position information to the wearer, even when he is not the pointman of the team and so the team is not intentionally spotting for him. With proper training this permits ranged attacks, even indirect fire, without neglecting the simultaneous squad doctrine obligation of covering the approach of the pointman."+  }+ring10 = ringTemplate+  { iname    = "the Ring"+  , ifreq    = [(TREASURE, 50), (ANY_JEWELRY, 50)]+  , irarity  = [(10, 4)]+  , iaspects = [ SetFlag Unique, ELabel "of Overwatch"+               , AddSkill SkProject 8  -- TODO: 11, but let player control+                                       -- potion throwing; see capReinforced+               , AddSkill SkMaxHP (-20)+               , SetFlag Durable, EqpSlot EqpSlotProject ]+               ++ iaspects ringTemplate+  , idesc    = "This exceptional medical contraption constantly transforms and re-injects minuscule amounts of blood serum, synthesizing powerful drugs that greatly enhance spacial awareness and focus, at the cost of weakening bodily resilience and recovery. With this boost, indirect fire becomes possible, even for a non-pointman team member."+  }++-- ** Armor++armorLeather = ItemKind+  { isymbol  = symbolTorsoArmor+  , iname    = "spacesuit jacket"+  , ifreq    = [ (COMMON_ITEM, 100), (S_SPACESUIT_JACKET, 1)+               , (SPACESUIT_PART, 1), (ARMOR_LOOSE, 1), (STARTING_ARMOR, 100) ]+  , iflavour = zipFancy [Blue]+  , icount   = 1+  , irarity  = [(1, 7), (10, 3)]+  , iverbHit = "thud"+  , iweight  = 7000+  , idamage  = 0+  , iaspects = [ AddSkill SkHurtMelee (-2)+               , AddSkill SkArmorMelee $ (2 + 1 `dL` 2) * 5+               , AddSkill SkArmorRanged $ (1 + 1 `dL` 2) * 3+               , SetFlag Durable, SetFlag Equipable+               , EqpSlot EqpSlotArmorMelee ]+  , ieffects = []+  , idesc    = "A hard-shell torso segment of a disposed off spacesuit. Well ventilated through the air tank outlets."+  , ikit     = []+  }+armorLeather2 = armorLeather  -- for now, purely flavour, for better messages+  { isymbol  = symbolMiscArmor+  , iname    = "pair"+  , ifreq    = [ (COMMON_ITEM, 100), (S_SPACESUIT_TROUSERS, 1)+               , (SPACESUIT_PART, 1), (STARTING_ARMOR, 100) ]+                 -- no ARMOR_LOOSE; harder to take off than a jacket+  , irarity  = [(3, 5), (10, 4)]+  , iaspects = ELabel "of spacesuit trousers" : iaspects armorLeather+  , idesc    = "Segmented trousers for open space work, with the hermetically sealed boots cut off. Surprisingly flexible and airy, yet micro-meteorite-proof."+  }+armorMail = armorLeather+  { iname    = "bulletproof vest"+  , ifreq    = [ (COMMON_ITEM, 100), (ARMOR_LOOSE, 1), (ARMOR_RANGED, 1)+               , (S_BULLTEPROOF_VEST, 1), (STARTING_ARMOR, 50) ]+  , iflavour = zipPlain [Cyan]+  , irarity  = [(4, 3), (7, 12), (10, 3)]+  , iweight  = 12000+  , idamage  = 0+  , iaspects = [ AddSkill SkHurtMelee (-5)+               , AddSkill SkArmorMelee $ (2 + 1 `dL` 2) * 5+               , AddSkill SkArmorRanged $ (3 + 1 `dL` 3) * 3+               , AddSkill SkOdor 2+               , SetFlag Durable, SetFlag Equipable+               , EqpSlot EqpSlotArmorRanged ]+  , ieffects = []+  , idesc    = "A civilian bulletproof vest. Discourages foes from attacking your torso, making it harder for them to land a blow. Really hard to wash due to thickness."+  }+meleeEnhancement = ItemKind+  { isymbol  = symbolTorsoArmor+  , iname    = "barebones exoskeleton"+  , ifreq    = [(COMMON_ITEM, 100)]+  , iflavour = zipFancy [Blue]+  , icount   = 1+  , irarity  = [(10, 10)]  -- many, but very varied quality+  , iverbHit = "zip"+  , iweight  = 1000+  , idamage  = 0+  , iaspects = [ AddSkill SkHurtMelee $ (2 + 1 `dL` 8) * 3+               , AddSkill SkArmorMelee 3+               , SetFlag Durable, SetFlag Equipable+               , EqpSlot EqpSlotHurtMelee ]+  , ieffects = []+  , idesc    = "A minimal frame from carbon fibre, designed to prevent injuries when lifting and operating heavy construction equipment. Cheap, light, disposable."+  , ikit     = []+  }+spacesuit = ItemKind+  { isymbol  = symbolTorsoArmor+  , iname    = "spacesuit"+  , ifreq    = [(S_SPACESUIT, 1)]+  , iflavour = zipFancy [BrWhite]+  , icount   = 1+  , irarity  = [(10, 10)]+  , iverbHit = "hug"+  , iweight  = 250000  -- including the fake gravity mass from two boots+  , idamage  = 0+  , iaspects = [ AddSkill SkHurtMelee (-30)  -- restricted arm movement+               , AddSkill SkSight (-1)+               , AddSkill SkHearing (-3), AddSkill SkSmell (-99)+               , AddSkill SkArmorMelee $ (8 + 1 `dL` 2) * 10  -- ~ 95+               , AddSkill SkArmorRanged $ (1 `dL` 3) * 12  -- ~ 25.5+                   -- both armors are sums of the full set, averaged;+                   -- the main advantage is taking only one slot+               , SetFlag Durable, SetFlag Equipable+               , EqpSlot EqpSlotArmorMelee ]+  , ieffects = []+  , idesc    = "A heavy spacesuit, with micro-suction machinery build into its boots, but requiring an external air tank for space walking. It heavily restrict arm movement, but not walking, regardless of gravity or lack thereof."+  , ikit     = []+  }+spacesuitTorn = spacesuit+  { iname    = "torn spacesuit"+  , ifreq    = [(CRAWL_ITEM, 100), (S_SPACESUIT_TORN, 1)]+  , icount   = 1 `d` 3+  , irarity  = [(1, 17), (8 * 10/15, 10)]+  , iverbHit = "entangle"+  , iweight  = 10000+  , iaspects = [ AddSkill SkHurtMelee (-30)+               , AddSkill SkSight (-1)  -- obstructed despite the tears+               , AddSkill SkArmorMelee $ (1 + 1 `d` 3) * 10  -- shallow, no `dL`+               , AddSkill SkArmorRanged $ (1 `d` 2) * 6+               , SetFlag Durable, SetFlag Equipable+               , EqpSlot EqpSlotArmorMelee ]+  , idesc    = "A badly torn spacesuit, barely offering any protection any more but still heavily restricting arm movement. Perhaps two decent wearable pieces could be salvaged by extracting, matching and patching components on a suitable workbench using scissors of some kind."+  , ikit     = [(BONDING_TOOL, CGround)]+  }+gloveFencing = ItemKind+  { isymbol  = symbolMiscArmor+  , iname    = "construction glove"+  , ifreq    = [ (COMMON_ITEM, 100), (ARMOR_MISC, 1), (ARMOR_RANGED, 1)+               , (STARTING_ARMOR, 50) ]+  , iflavour = zipPlain [BrGreen]+  , icount   = 1+  , irarity  = [(6, 9), (10, 5)]+  , iverbHit = "flap"+  , iweight  = 100+  , idamage  = 2 `d` 1+  , iaspects = [ AddSkill SkHurtMelee $ (2 + 1 `d` 2 + 1 `dL` 2) * 3+               , AddSkill SkArmorRanged $ (1 `dL` 2) * 3+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotHurtMelee+               , toVelocity 40 ]  -- flaps and flutters+  , ieffects = []+  , idesc    = "A flexible construction glove from rough leather ensuring a good grip. Also, quite effective in averting or even catching slow projectiles."+  , ikit     = []+  }+gloveGauntlet = gloveFencing+  { iname    = "spacesuit glove"+  , ifreq    = [ (COMMON_ITEM, 100), (S_SPACESUIT_GLOVE, 1)+               , (SPACESUIT_PART, 2), (ARMOR_MISC, 1), (STARTING_ARMOR, 50) ]+  , iflavour = zipFancy [White]+  , irarity  = [(1, 10), (5 * 10/15, 10), (6 * 10/15, 1)]+  , iverbHit = "mow"+  , iweight  = 500+  , idamage  = 3 `d` 1+  , iaspects = [ AddSkill SkArmorMelee $ (1 `d` 3) * 5+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponFast  -- no timeout, so worth wielding+               , toVelocity 40 ]  -- flaps and flutters+  , idesc    = "A piece of a hull maintenance spacesuit, padded, reinforced with carbon fibre, with extruding titan manipulators."+  }+gloveJousting = gloveFencing+  { iname    = "Welding Handgear"+  , ifreq    = [(COMMON_ITEM, 100), (ARMOR_MISC, 1)]+  , iflavour = zipFancy [BrRed]+  , irarity  = [(1, 6), (10, 3)]+  , iverbHit = "ram"+  , iweight  = 3000+  , idamage  = 5 `d` 1+  , iaspects = [ SetFlag Unique+               , Timeout 5+               , AddSkill SkHurtMelee $ (-5 + 1 `dL` 3) * 5+               , AddSkill SkArmorMelee $ (2 + 1 `d` 2 + 1 `dL` 3) * 5+               , AddSkill SkArmorRanged $ (1 + 1 `dL` 3) * 3+                 -- very random on purpose and can even be good on occasion+                 -- or when ItemRerolled enough times+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponFast  -- hope to replace with better soon+               , toVelocity 60 ]  -- flaps and flutters+  , idesc    = "Rigid, bulky handgear embedding a defunct welding equipment, complete with an affixed small shield and a darkened visor. Awe-inspiring."+  }+hatUshanka = ItemKind+  { isymbol  = symbolMiscArmor+  , iname    = "ushanka hat"+  , ifreq    = [ (COMMON_ITEM, 100), (ARMOR_MISC, 1), (CLOTHING_MISC, 1)+               , (STARTING_ARMOR, 50) ]+  , iflavour = zipPlain [Brown]+  , icount   = 1+  , irarity  = [(3, 7), (10, 4)]+  , iverbHit = "tickle"+  , iweight  = 500+  , idamage  = 0+  , iaspects = [ Timeout $ (2 + 1 `d` 2) * 3+               , AddSkill SkArmorMelee 5, AddSkill SkHearing (-6)+               , SetFlag Periodic, SetFlag Durable, SetFlag Equipable+               , EqpSlot EqpSlotArmorMelee+               , toVelocity 50 ]  -- flaps and flutters+  , ieffects = [RefillCalm 1]+  , idesc    = "Soft and warm fur. It keeps your ears warm."+  , ikit     = []+  }+capReinforced = ItemKind+  { isymbol  = symbolMiscArmor+  , iname    = "construction cap"+  , ifreq    = [(COMMON_ITEM, 100), (ARMOR_MISC, 1), (STARTING_ARMOR, 50)]+  , iflavour = zipPlain [BrYellow]+  , icount   = 1+  , irarity  = [(6, 9), (10, 3)]+  , iverbHit = "cut"+  , iweight  = 1000+  , idamage  = 0+  , iaspects = [ AddSkill SkArmorMelee $ (1 `d` 3) * 5+               , AddSkill SkProject 1+                   -- the brim shields against blinding by light sources, etc.;+                   -- beware of stacking and causing auto-fling of vials+               , SetFlag Durable, SetFlag Equipable+               , EqpSlot EqpSlotProject ]+  , ieffects = []+  , idesc    = "A hard plastic shell that might soften a blow."+  , ikit     = []+  }+helmArmored = ItemKind+  { isymbol  = symbolMiscArmor+  , iname    = "spacesuit helmet"+  , ifreq    = [ (COMMON_ITEM, 100), (S_SPACESUIT_HELMET, 1)+               , (SPACESUIT_PART, 1), (ARMOR_MISC, 1), (ARMOR_RANGED, 1)+               , (STARTING_ARMOR, 50) ]+  , iflavour = zipFancy [BrBlue]+  , icount   = 1+  , irarity  = [(1, 11), (4 * 10/15, 11), (5 * 10/15, 1)]+  , iverbHit = "headbutt"+  , iweight  = 2000+  , idamage  = 4 `d` 1+  , iaspects = [ Timeout 4+               , AddSkill SkArmorMelee $ (1 `dL` 3) * 5+               , AddSkill SkArmorRanged $ (2 + 1 `dL` 2) * 3  -- headshot+               , AddSkill SkSight (-1)+               , AddSkill SkHearing (-3), AddSkill SkSmell (-5)+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotArmorRanged+               , toVelocity 50 ]  -- unwieldy+  , ieffects = []+  , idesc    = "Blocks out everything, including your senses."+  , ikit     = []+  }+heavyBoot = ItemKind+  { isymbol  = symbolMiscArmor+  , iname    = "spacesuit boot"+  , ifreq    = [ (COMMON_ITEM, 100), (S_SPACESUIT_BOOT, 1)+               , (SPACESUIT_PART, 2), (ARMOR_MISC, 1) ]+                 -- no STARTING_ARMOR, because the malus tricky for newbies+  , iflavour = zipFancy [Magenta]+  , icount   = 1+  , irarity  = [(1, 12), (3 * 10/15, 12), (4 * 10/15, 1)]+  , iverbHit = "sock"+  , iweight  = 100000  -- including the fake gravity mass+  , idamage  = 6 `d` 1+  , iaspects = [ Timeout 7+               , AddSkill SkHurtMelee (-10)+               , AddSkill SkArmorMelee $ (1 + 1 `dL` 3) * 5+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponFast  -- hope to replace with better soon+               , toVelocity 500 ]  -- the fake mass not counted for throwing+  , ieffects = []+  , idesc    = "An armored boot, cut-off from a spacesuit. The in-built micro-suction machinery for maintaining traction in the absence of gravity gives stability equivalent to an extra 100kg of mass. Kicks get abrupt acceleration millimeters short of the target."+  , ikit     = []+  }+ragTangle = sandstoneRock+  { isymbol  = symbolClothes+  , iname    = "tangle"+  , ifreq    = [ (COMMON_ITEM, 10), (CLOTH_RAG, 1), (THICK_CLOTH, 1)+               , (S_RAG_TANGLE, 1), (UNREPORTED_INVENTORY, 1) ]+  , iflavour = zipPlain [Brown]+  , icount   = 1+  , irarity  = [(1, 10)]  -- crafted, so rare+  , iverbHit = "touch"+  , iweight  = 200+  , idamage  = 0+  , iaspects = [ ELabel "of rags"+               , SetFlag Fragile, AddSkill SkArmorMelee 2+               , SetFlag Equipable, EqpSlot EqpSlotArmorMelee ]+  , idesc    = "Fashionable --- sometimes. Useful for survival crafting, for example as a wick of a makeshift oil lamp --- always."+  }+-- The biggest power of bucklers and shields is the ranged deflection.+-- There is a risk of micro-management, when the player takes off+-- a buckler or shield just after it activated at ranged attack,+-- regaining the speed for the probably upcoming melee fight.+-- This has the drawback that, while the shield recharges in stash,+-- it looses the charge when equipped again and if it's activated+-- from the stash, the player suffers its full piercing damage.+-- OTOH, if the player has maximized armor, he only suffers 5%+-- of the pierced damage of the shield. But to gain such high armor+-- usually requires keeping a shield equipped.+-- And the speed loss can't be avoided when exploring+-- and an extra slowdown comes from unequipping and equipping again+-- after melee, so it starts to be a meaningful and situational+-- trade-off and not a meaningless micro-management. If players+-- suffer from micro-management anyway, the recharging effect can be+-- limited to hitting in melee only (@ActivationMeleeable@).+-- The push effect is applied to self outside melee, so even less problematic.+buckler = ItemKind+  { isymbol  = symbolShield+  , iname    = "buckler"+  , ifreq    = [(COMMON_ITEM, 100), (ARMOR_LOOSE, 1)]+  , iflavour = zipPlain [Blue]+  , icount   = 1+  , irarity  = [(4, 7)]+  , iverbHit = "bash"+  , iweight  = 2000+  , idamage  = 2 `d` 1+  , iaspects = [ Timeout $ (5 + 1 `d` 2 - 1 `dL` 2) * 2+               , AddSkill SkArmorMelee 40+               , AddSkill SkSpeed (-1)  -- the main price to pay+               , SetFlag UnderRanged, SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotArmorMelee+               , toVelocity 50 ]  -- unwieldy to throw+  , ieffects =+      [ IfThenElse (TriggeredBy ActivationUnderRanged)+                   (NopEffect `OrEffect` toOrganGood S_RANGED_DEFLECTING 1)+                     -- this is particularly useful when exploring+                     -- and getting ambushed; @OrEffect@ is only to help AI+                     -- understand it won't be applied to foes,+                     -- but prevent it from assigning too much value+                     -- and so attacking with buckler too early+                   (OnUser (Recharge 4 20)) ]+                     -- this is useful during a fight+  , idesc    = "An arm protection made from an outer airlock panel. Not too small to deflect projectiles occasionally. Almost harmless when used offensively, but makes room for other weapons."+  , ikit     = []+  }+-- In melee, the shield's biggest power is pushing enemies,+-- which however reduces to 1 extra damage point if no clear space behind enemy.+-- So they require keen tactical management.+-- Note that AI will pick them up but never wear and will use them at most+-- as a way to push itself. Despite being @Meleeable@, they will not be used+-- as weapons either. This is OK, using shields smartly is totally beyond AI.+shield = buckler+  { iname    = "shield"+  , irarity  = [(7, 5)]  -- the stronger variants add to total probability+  , iflavour = zipPlain [Green]+  , iweight  = 4000+  , idamage  = 3 `d` 1+  , iaspects = [ Timeout $ (7 + 1 `d` 2 - 1 `dL` 2) * 2+               , AddSkill SkArmorMelee 60+               , AddSkill SkSpeed (-1)  -- the main price to pay+               , AddSkill SkHurtMelee (-25)+               , SetFlag UnderRanged, SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotArmorMelee+               , toVelocity 50 ]  -- unwieldy to throw+  , ieffects =+      [ IfThenElse (TriggeredBy ActivationUnderRanged)+                   (OnUser (toOrganGood S_RANGED_DEFLECTING 1))+                     -- this is particularly useful when exploring+                     -- and getting ambushed; @OnUser@ is only to help AI+                     -- understand it won't be applied to foes+                   (PushActor (ThrowMod 200 50 1)) ]  -- 1 step, fast+                     -- this is useful during a fight+  , idesc    = "An unwieldy rectangle made of anti-meteorite ceramic sheet. Absorbs a percentage of melee damage, both dealt and sustained. Large enough to shield against projectiles for as long as there is strength to keep it poised. Requires particularly keen positional awareness when used as a weapon."+  }+shield2 = shield+  { ifreq    = [(COMMON_ITEM, 20), (MUSEAL, 100), (S_SHIELD_BLUNT, 1)]+  , iweight  = 6000+  , idamage  = 4 `d` 1+  , ieffects =+      [ IfThenElse (TriggeredBy ActivationUnderRanged)+                   (OnUser (toOrganGood S_RANGED_DEFLECTING 1))+                   (PushActor (ThrowMod 400 50 1)) ]  -- 2 steps, fast+  , idesc    = "A relic of long-past wars, heavy and with a central spike, which is however misaligned and dull."+  }+shield3 = shield2+  { ifreq    = [(COMMON_ITEM, 1), (MUSEAL, 3), (S_SHIELD_SHARP, 1)]+  , idamage  = 7 `d` 1+  , idesc    = "A relic of long-past wars, heavy and with a sharp central spike."+  }++-- ** Weapons++-- Generally, weapons on long poles have highest damage and defence,+-- but longest timeout. Weapons with handles are middling. Weapons+-- without area weakest, but lowest timeout and highest global melee bonus.+-- Weapons of a given group tend to share the weakest representative's+-- characteristics, even when upgraded. Sharpening of weapons usually+-- just increases their damage.++-- For some weapons the main damage is not edged nor piercing,+-- but wounding trough impact or burns. A portion of such weapons+-- also double as tools for terrain transformation or crafting.++blowtorch = ItemKind+  { isymbol  = symbolTool+  , iname    = "blowtorch"  -- not unique, but almost never generated on floor+  , ifreq    = [ (BLOWTORCH, 1), (VALUABLE, 20)  -- make AI afraid to hog+               , (CRAWL_ITEM, 1)  -- @PolyItem@ doesn't work on it+               , (BREACHING_TOOL, 1), (FIRE_SOURCE, 1) ]+                 -- infinite use, but harmful+  , iflavour = zipPlain [BrRed]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "scorch"+  , iweight  = 2000+  , idamage  = 0+  , iaspects = [ Timeout 4+               , AddSkill SkAlter 2+               , AddSkill SkWait (-2)+                   -- patience thin in the heat; prevents sleep, to let+                   -- AI use @SkAlter@ to get to level 4+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotAlter+               , toVelocity 0 ]  -- @Burn@ not effective when thrown+  , ieffects = [ Burn 3  -- ensure heroes wear initially, so they reach lvl 4+               , Impress ]+      -- is used for melee in precedence to fists, but not to cleavers;+      -- so if player wants to hit with it, it's enough to pack other gear;+      -- is also the low bar for self-inflicted damage from durable breaching+      -- tool and fire source use so that other tool-weapons need only+      -- do that many non-armor affected damage to dissuade the player+      -- from using them without careful thought+  , idesc    = "A sturdy old-fashioned portable blowtorch for fine cutting or welding of metals. Unfocused and inaccurate, but does not require access codes to high current power outlets. If you can patiently suffer the heat, it can be used as a clumsy breaching tool for stuck, locked and welded containers and doors."  -- for some reason I don't feel like being more obvious here about the use for the welded staircase; maybe that's because the torch can be used long after the first puzzle of the game is forgotten and so the in-you-face hints should be on items and actors that get forgotten together with the puzzle+  , ikit     = []+  }+laserSharpener = ItemKind+  { isymbol  = symbolTool+  , iname    = "Laser Sharpener"+  , ifreq    = [(CRAWL_ITEM, 50), (SHARPENING_TOOL, 1)]+  , iflavour = zipFancy [BrBlue]+  , icount   = 1+  , irarity  = [(5, 25)]  -- comes bundled with other tools+  , iverbHit = "paint"+  , iweight  = 2000+  , idamage  = 0+  , iaspects = [ SetFlag Unique, Timeout 5+               , SetFlag Durable, SetFlag Meleeable, EqpSlot EqpSlotWeaponBig+               , toVelocity 0 ]  -- @Burn@ not effective when thrown+  , ieffects = [Burn 4]  -- really harmful when used as a sharpener; intended+  , idesc    = "Laser ablation is the safest and most accurate of sharpening method. Misaligned optics with broken shielding, however, change the situation dramatically, enabling stray laser pulses to escape at unpredictable angles."  -- hence short range and so melee weapon; TODO: long range weapon with instant projectiles and no risk of hull breach+  , ikit     = []+  }+crowbar = ItemKind+  { isymbol  = symbolTool+  , iname    = "crowbar"+  , ifreq    = [ (COMMON_ITEM, 100), (BREACHING_TOOL, 1), (S_CROWBAR, 1)+               , (STARTING_WEAPON, 30) ]+  , iflavour = zipPlain [BrCyan]+  , icount   = 1+  , iweight  = 1000+  , irarity  = [(1, 5), (3 * 10/15, 5), (4 * 10/15, 1)]+  , iverbHit = "gouge"+  , idamage  = 2 `d` 1+  , iaspects = [ Timeout $ 2 + 1 `d` 3+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponFast+               , toVelocity 0 ]  -- totally unbalanced+  , ieffects = [RefillHP (-3)]+                 -- @RefillHP@ to avoid a no-brainer of durable tool use;+                 -- (idamage ignored to avoid the exploit of tool use in armor)+  , idesc    = "This is a heavy and pointy piece of steel that can be employed as an improvised melee weapon. It is also usable as a breaching tool, though rather injurious."  -- TODO: https://en.wikipedia.org/wiki/Crowbar_(tool)+  , ikit     = []+  }+catsPaw = ItemKind+  { isymbol  = symbolTool+  , iname    = "cat's paw"+  , ifreq    = [ (COMMON_ITEM, 100), (BREACHING_TOOL, 1)+               , (STARTING_WEAPON, 15) ]+  , iflavour = zipPlain [Cyan]+  , icount   = 1+  , iweight  = 500+  , irarity  = [(1, 12), (3 * 10/15, 12), (4 * 10/15, 1)]+  , iverbHit = "paw"+  , idamage  = 1 `d` 1+  , iaspects = [ Timeout $ 1 + 1 `d` 2+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponFast+               , toVelocity 0 ]  -- totally unbalanced+  , ieffects = [RefillHP (-2)]+                 -- @RefillHP@ to avoid a no-brainer of durable tool use;+                 -- also quite attractive as a ranged weapon+  , idesc    = "This is a sturdy and pointy piece of steel that can be employed as an improvised melee weapon. It is also usable as a breaching tool, though not a particularly safe one."  -- TODO: https://en.wikipedia.org/wiki/Cat%27s_paw_(nail_puller)+  , ikit     = []+  }+shortClub = ItemKind+  { isymbol  = symbolHafted+  , iname    = "short club"+  , ifreq    = [(S_SHORT_CLUB, 1), (STARTING_WEAPON, 700)]+  , iflavour = zipPlain [BrBlue]+  , icount   = 1+  , irarity  = [(1, 1)]  -- only crafted+  , iverbHit = "club"+  , iweight  = 2500+  , idamage  = 2 `d` 1+  , iaspects = [ SetFlag Durable, SetFlag Meleeable, EqpSlot EqpSlotWeaponFast+               , toVelocity 60 ]+  , ieffects = [RefillHP (-1)]+  , idesc    = "Simplicity."+  , ikit     = []+  }+longClub = shortClub+  { iname    = "long club"+  , ifreq    = [(S_LONG_CLUB, 1), (STARTING_WEAPON, 500)]+  , iflavour = zipPlain [Magenta]+  , iweight  = 3500+  , idamage  = 3 `d` 1  -- from two scraps+  , iaspects = [ Timeout $ 2 + 1 `d` 2+               , SetFlag Durable, SetFlag Meleeable, EqpSlot EqpSlotWeaponFast+               , toVelocity 0 ]  -- totally unbalanced+  , ieffects = [RefillHP (-2)]+  , idesc    = "Simplicity, long version."+  }+hammerTemplate = ItemKind  -- properly hafted *and* glued to handle/pole+  { isymbol  = symbolHafted+  , iname    = "sledgehammer"  -- "demolition hammer" is Br. Eng. for jackhammer+  , ifreq    = [(HAMMER_UNKNOWN, 1)]+                 -- not @BREACHING_TOOL@, because it trigger traps+                 -- and destroys treasure, instead of opening; generally+                 -- a very aggressive weapon, bad for defense even when long+  , iflavour = zipFancy [BrMagenta]  -- avoid "pink"+  , icount   = 1+  , irarity  = [(1, 2), (3, 2), (7, 25), (9, 1)]+                 -- not too common up to lvl 3, where one is guaranteed+                 -- and deep down, when crafting done already+  , iverbHit = "club"+  , iweight  = 4000+  , idamage  = 0  -- all damage independent of melee skill; this also helps+                  -- not to lie about damage of unidentified items+  , iaspects = [ PresentAs HAMMER_UNKNOWN+               , SetFlag Durable, SetFlag Meleeable, EqpSlot EqpSlotWeaponBig+               , toVelocity 0 ]  -- totally unbalanced+  , ieffects = []+  , idesc    = "One of many kinds of hammers employed in construction work. The usual ones with blunt heads don't cause grave wounds, but enough weight on a long handle can shake and bruise even most armored foes. However, larger hammers require more time to recover after a swing."  -- replaced with one of the descriptions below at identification time+  , ikit     = []+  }+hammer1 = hammerTemplate  -- 1m handle, blunt+  { ifreq    = [ (COMMON_ITEM, 100)+               , (STARTING_WEAPON, 50), (STARTING_HAMMER, 80)+               , (S_SHORT_BLUNT_HAMMER, 1) ]+  , iaspects = Timeout 6+               : iaspects hammerTemplate+  , ieffects = [RefillHP (-5)]  -- weak, but meant to be sharpened ASAP+  , idesc    = "One of many kinds of hammers employed in construction work. This is the usual one, with a blunt head and a short handle that, with a vice, may be pushed out and replaced with a longer pole."+  }+hammer2 = hammerTemplate  -- 0.75m handle, sharp+  { ifreq    = [ (COMMON_ITEM, 10)+               , (STARTING_WEAPON, 5), (STARTING_HAMMER, 5)+               , (BONDING_TOOL, 1) ]+  , irarity  = [(1, 4), (3, 4), (7, 40)]+                 -- common early, since not a guaranteed drop;+                 -- common also late, because not crafted+  , iverbHit = "puncture"+  , idamage  = 3 `d` 1+  , iaspects = [Timeout 3, EqpSlot EqpSlotWeaponFast]+               ++ (iaspects hammerTemplate \\ [EqpSlot EqpSlotWeaponBig])+  , ieffects = [RefillHP (-3)]+  , idesc    = "Upon closer inspection, this hammer, or pick, turns out particularly well balanced. The profiled handle seamlessly joins the head, which focuses the blow at a sharp point, compensating for the tool's modest size. This makes it capable of permanently smashing objects together, though any fumble results in hands smashed as well."+  }+hammer3 = hammerTemplate  -- 2m pole, blunt+  { ifreq    = [ (COMMON_ITEM, 4), (STARTING_WEAPON, 2)+               , (S_LONG_BLUNT_HAMMER, 1) ]+  , iweight  = 6000  -- pole weight almost gives it away+  , iaspects = [ Timeout 12+               , AddSkill SkHurtMelee $ (-6 + 1 `d` 4) * 3 ]+                   -- the malus not so important, hence easy to get the best+               ++ iaspects hammerTemplate+  , ieffects = [RefillHP (-8)]+  , idesc    = "This maul sports a particularly long pole that increases the momentum of the blunt head's swing, at the cost of long recovery."+  }+hammer4 = hammer1  -- 1m handle, sharp+  { ifreq    = [ (COMMON_ITEM, 4), (STARTING_WEAPON, 2)+               , (S_SHORT_SHARP_HAMMER, 1) ]+  , iverbHit = "cleave"+  , idamage  = 3 `d` 1+  , idesc    = "This hammer's head has it's protruding edges sharpened. Otherwise, it's pretty ordinary, with a short handle."+ }+hammer5 = hammer3  -- 2m pole, sharp+  { ifreq    = [(COMMON_ITEM, 1), (S_LONG_SHARP_HAMMER, 1)]+  , iverbHit = "cleave"+  , idamage  = 3 `d` 1+  , idesc    = "This maul features a head with the edge of the narrow end sharpened for cutting. Such long-hafted hammers require more time to recover after a swing, but the momentum alone can shake and bruise even armored foes that can't be harmed by sharp edges."+  }+hammerParalyze = hammerTemplate+  { iname    = "The Concussion Hammer"+  , ifreq    = [(TREASURE, 40), (STARTING_HAMMER, 5)]+  , irarity  = [(5, 1), (8, 6)]+  , idamage  = 3 `d` 1+  , iaspects = [ SetFlag Unique+               , Timeout 10 ]  -- 2m, but light head and pole+               ++ iaspects hammerTemplate+  , ieffects = [RefillHP (-8), Paralyze 10]+  , idesc    = "This exquisite demolition hammer with a titanium head and exceptionally long synthetic handle leaves no wall and no body standing."+  }+hammerSpark = hammerTemplate  -- the only hammer with significantly heavier head+  { iname    = "The Grand Smithhammer"+  , ifreq    = [(TREASURE, 40), (BONDING_TOOL, 1), (MUSEAL, 100)]+  , irarity  = [(5, 1), (8, 6)]+  , iweight  = 5000  -- weight and shape/damage gives it away; always identified+  , iaspects = [ SetFlag Unique+               , Timeout 8  -- 1.5m handle and heavy, but unique+               , AddSkill SkHurtMelee $ (-20 + 1 `dL` 10) * 5  -- 50--95+               , EqpSlot EqpSlotWeaponBig+               , SetFlag Durable, SetFlag Meleeable+               , toVelocity 0 ]  -- totally unbalanced+  , ieffects = [ Explode S_SPARK+                   -- we can't use a focused explosion, because it would harm+                   -- the hammer wielder as well, unlike this one; it's already+                   -- risky, since it may alert other factions; can reveal+                   -- other enemies rarely, though+               , RefillHP (-15) ]  -- hammer tanks prefer consistent damage+                                   -- over 1-shot kills, so this is not OP+  , idesc    = "High carbon steel of this heavy old hammer doesn't yield even to the newest alloys and produces fountains of sparks in defiance. Whatever it forge-welds together, stays together. Don't try to use it without training, however."+  }++-- The standard melee weapons do most of their damage as kinetic edged+-- or piercing, affected by armor and hurt skill.++knife = ItemKind+  { isymbol  = symbolEdged+  , iname    = "cleaver"+  , ifreq    = [(COMMON_ITEM, 100), (STARTING_WEAPON, 100), (S_CLEAVER, 1)]+  , iflavour = zipPlain [BrCyan]+  , icount   = 1+  , irarity  = [(1, 3), (3, 3), (5, 40), (9, 1)]+                 -- useful initially and for crafting mid-game+  , iverbHit = "cut"+  , iweight  = 1000+  , idamage  = 5 `d` 1+  , iaspects = [ Timeout 3+               , AddSkill SkHurtMelee $ (1 `d` 2 + 1 `dL` 3) * 3+                   -- very common, so don't make too random nor too good+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponFast+               , toVelocity 40 ]  -- ensuring it hits with the tip costs speed+  , ieffects = []+  , idesc    = "A heavy professional kitchen blade. Will do fine cutting any kind of meat, bone and an occasional metal can. Does not penetrate deeply, but is quick to move and hard to block. Especially useful in conjunction with a larger weapon."+  , ikit     = []+  }+daggerDropBestWeapon = knife+  { iname    = "The Double Dagger"+  , ifreq    = [(TREASURE, 40), (MUSEAL, 100)]+  , irarity  = [(1, 3), (10, 2)]+  , iaspects = SetFlag Unique+               : iaspects knife+  , ieffects = [Discharge 1 50, Yell]  -- powerful and low timeout, but noisy+                                       -- and no effect if no weapons charged+  , idesc    = "An antique double dagger that a focused fencer can use to catch and twist away an opponent's blade."+  }+dagger = knife+  { iname    = "dagger"+  , ifreq    = [(COMMON_ITEM, 4), (S_DAGGER, 1), (STARTING_WEAPON, 4)]+  , iverbHit = "open"+  , irarity  = [(7, 20)]  -- like hammer, not knife, to prevent excess+  , idamage  = 7 `d` 1+  , idesc    = "A double-edged knife with a sharp tip that penetrates the smallest defence gaps, making it especially useful in conjunction with a larger but less nimble weapon."+  }+sword = ItemKind  -- dead end, but can be crafted with just one file tool+  { isymbol  = symbolPolearm+  , iname    = "sharpened pipe"+  , ifreq    = [ (COMMON_ITEM, 4), (STARTING_WEAPON, 30)+               , (S_SHARPENED_PIPE, 1) ]+  , iflavour = zipFancy [BrCyan]+  , icount   = 1+  , irarity  = [(3, 1), (6, 15)]+  , iverbHit = "stab"+  , iweight  = 2000+  , idamage  = 10 `d` 1  -- with high melee bonus, better than a good hammer+  , iaspects = [ Timeout 7  -- unique in that there's no randomness+               , EqpSlot EqpSlotWeaponBig+               , SetFlag Durable, SetFlag Meleeable+               , toVelocity 40 ]  -- ensuring it hits with the tip costs speed+  , ieffects = []+  , idesc    = "A makeshift weapon of simple design, but great potential."+  , ikit     = []+  }+swordImpress = sword+  { isymbol  = symbolEdged+  , iname    = "The Master's Sword"+  , ifreq    = [(TREASURE, 40), (MUSEAL, 100)]+  , irarity  = [(5, 1), (8, 6)]+  , iverbHit = "slash"+  , iaspects = SetFlag Unique+               : iaspects sword+  , ieffects = [Impress]+  , idesc    = "A particularly well-balanced museum piece. It has a history and in the right hands lends itself to impressive shows of fencing skill."+  }+swordNullify = sword+  { isymbol  = symbolEdged+  , iname    = "The Blunt Rapier"+  , ifreq    = [(TREASURE, 50), (S_RAPIER_BLUNT, 1)]+  , iverbHit = "pierce"+  , irarity  = [(5, 1), (8, 6)]+  , idamage  = 7 `d` 1  -- as dagger, but upgradeable and no skill bonus+  , iaspects = [ SetFlag Unique, Timeout 3, EqpSlot EqpSlotWeaponFast+               , SetFlag Durable, SetFlag Meleeable+               , toVelocity 40 ]  -- ensuring it hits with the tip costs speed+  , ieffects = []+  , idesc    = "An exuberant hand-forged roasting implement, intentionally and wisely kept blunt."+  }+swordNullifySharp = swordNullify+  { iname    = "The Roasting Rapier"+  , ifreq    = [(S_RAPIER_SHARP, 1)]+  , idamage  = 10 `d` 1+  , ieffects = [ DropItem 1 maxBound COrgan CONDITION+               , RefillCalm (-10)+               , Yell ]+  , idesc    = "A thin, acutely sharp steel blade that pierces deeply and sends its victim into abrupt, sobering shock. Originally, an exuberant hand-forged roasting implement, intentionally and wisely kept blunt."+  }+halberd = ItemKind  -- long pole+  { isymbol  = symbolPolearm+  , iname    = "pole cleaver"+  , ifreq    = [(COMMON_ITEM, 3), (STARTING_WEAPON, 70), (S_POLE_CLEAVER, 1)]+  , iflavour = zipPlain [BrYellow]+  , icount   = 1+  , irarity  = [(5, 1), (10, 3)]+  , iverbHit = "slice"+  , iweight  = 3500+  , idamage  = 11 `d` 1  -- bad, until sharpened+  , iaspects = [ Timeout 10+               , AddSkill SkHurtMelee $ (-12 + 1 `d` 4 + 1 `dL` 5) * 3+                   -- weak against armor at game start with no hurt bonus;+                   -- variety to spice up crafting+               , AddSkill SkArmorMelee 20+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponBig+               , toVelocity 20 ]  -- not balanced+  , ieffects = []+  , idesc    = "An improvised but deadly weapon made of a long kitchen cleaver glued and bound to a long pole. Not often one succeeds in making enough space to swing it freely, but even when stuck between terrain obstacles it blocks approaches effectively and makes using other weapons difficult, both by friends and foes."+  , ikit     = []+  }+oxTongue = halberd  -- long pole, because glued 1m handle worse than nothing+  { iname    = "long spear"+  , ifreq    = [(COMMON_ITEM, 1), (S_LONG_SPEAR, 1)]+  , iverbHit = "impale"+  , idamage  = 13 `d` 1+  , idesc    = "An improvised but deadly weapon made of a long, sharp dagger glued and bound to a long pole. Not often one succeeds in making enough space to thrust it freely, but even when stuck between terrain obstacles it blocks approaches effectively and makes using other weapons difficult, both by friends and foes."+  }+halberdPushActor = halberd+  { iname    = "The Blunt Swiss Halberd"+  , ifreq    = [(CRAWL_ITEM, 30), (S_HALBERD_BLUNT, 1)]+                 -- not in a museum; reenactors' gear+  , irarity  = [(7, 0), (9, 8)]+  , iaspects = SetFlag Unique+               : iaspects halberd+  , ieffects = [PushActor (ThrowMod 200 100 1)]  -- 2 steps, slow+  , idesc    = "A perfect replica made for a reenactor troupe, hardened, but missing any sharpening. Versatile, with great reach and leverage. Foes are held at a distance."+  }+halberdPushActorSharp = halberdPushActor+  { iname    = "The Swiss Halberd"+  , ifreq    = [(S_HALBERD_SHARP, 1)]+  , idamage  = 13 `d` 1+  , idesc    = "A perfect replica made for a reenactor troupe, hardened, sharpened. Versatile, with great reach and leverage. Foes are held at a distance."+  }+fireAxe = ItemKind+  { isymbol  = symbolHafted+  , iname    = "fire axe"+  , ifreq    = [(TREASURE, 20), (S_FIRE_AXE, 1), (FIRE_FIGHTING_ITEM, 5)]+                 -- not @BREACHING_TOOL@, because it trigger traps+                 -- and destroys treasure, instead of opening+  , iflavour = zipPlain [BrRed]+  , icount   = 1+  , irarity  = [(1, 1)]  -- from fire cabinets+  , iverbHit = "gouge"+  , iweight  = 1600+  , idamage  = 9 `d` 1  -- worse than sharpened pipe, but upgradable+  , iaspects = [ Timeout 7+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponBig  -- 1m handle+               , toVelocity 40 ]  -- ensuring it hits with the blade costs speed+  , ieffects = []+  , idesc    = "An axe with a spike: once used for fire fighting, now turned to a bloodier purpose."+  , ikit     = []+  }+pollaxe = fireAxe+  { iname    = "pollaxe"+  , ifreq    = [(TREASURE, 2), (S_POLL_AXE, 1)]+  , iflavour = zipPlain [BrRed]+  , iverbHit = "carve"+  , iweight  = 4500+  , idamage  = 15 `d` 1+  , iaspects = [ Timeout $ 15 - 1 `dL` 5  -- variety to spice up crafting+               , AddSkill SkArmorMelee 20+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponBig+               , toVelocity 20 ]  -- not balanced+  , ieffects = [OnUser (Discharge 5 50)]+  , idesc    = "A long-hafted spiked axe: great reach and momentum, but so unbalanced that fighters swinging it can't control their combat stance."+  }+militaryKnife = knife+  { iname    = "military knife"+  , ifreq    = [ (TREASURE, 1), (WIRECUTTING_TOOL, 2), (MERCENARY_WEAPON, 70)+               , (STARTING_WEAPON, 3) ]+  , iflavour = zipFancy [Green]+  , irarity  = [(10, 15)]  -- in crawl, comes bundled with tools+  , iweight  = 500  -- too small to attach to a pole+  , idamage  = 7 `d` 1+  , iaspects = [ Timeout 2+               , AddSkill SkHurtMelee $ (-1 + 1 `d` 3 + 1 `dL` 2) * 3+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponFast+               , toVelocity 50 ]  -- designed also for throwing+  , ieffects = [ RefillHP (-1)+                   -- @RefillHP@ to avoid a no-brainer of durable tool use+               , DropItem 1 maxBound COrgan CONDITION ]+                   -- useful for AI who is the main user of this weapon+  , idesc    = "Military design laser-sharpened alloy blade able to cleanly open an artery at the lightest touch through layers of fabric. Despite its modest size, it defeats barbed wire in one slice."+  }+militaryBaton = ItemKind+  { isymbol  = symbolHafted+  , iname    = "military stun gun"+  , ifreq    = [(TREASURE, 1), (MERCENARY_WEAPON, 30)]+  , iflavour = zipFancy [Green]+  , icount   = 1+  , irarity  = [(10, 10)]+  , iverbHit = "prod"+  , iweight  = 1000+  , idamage  = 6 `d` 1+  , iaspects = [ Timeout 7+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponBig+               , toVelocity 40 ]+  , ieffects = [Discharge 1 100, RefillCalm (-50)]+      -- don't overdo discharging or unequipping becomes beneficial+  , idesc    = "A direct contact electroshock weapon with unlimited and fast recharging. Ideal for close quarter fights inside space habitats, where preserving the integrity of the outer hull is paramount."+  , ikit     = []+  }+cattleProd = militaryBaton+  { iname    = "electric cattle prod"+  , ifreq    = [(COMMON_ITEM, 100)]+  , iflavour = zipPlain [Brown]+  , irarity  = [(8, 5)]+  , idamage  = 5 `d` 1+  , ieffects = [Discharge 1 60, RefillCalm (-30)]+      -- The Calm effect helps to place it as one of the first weapons.+  , idesc    = "Used for subduing unruly zoo animals."+  }++-- The combat value of the following class of weapons is very limited,+-- but they or their components can be used for crafting.++gardenMsg :: Effect+gardenMsg = VerbMsgFail "feel the gardening tool fracture" "."+gardenDestruct :: GroupName ItemKind -> Effect+gardenDestruct grp =+  OnUser $ OneOf $+    DestroyItem 1 1 CEqp grp+    `AndEffect`+    SeqEffect [ CreateItem Nothing CStash HANDLE timerNone+              , CreateItem Nothing CStash STEEL_SCRAP timerNone ]+    : replicate 3 gardenMsg+grassStitcher = ItemKind+  { isymbol  = symbolPolearm+  , iname    = "grass stitcher"+  , ifreq    = [ (COMMON_ITEM, 10), (HANDLE_AND_STEEL, 1)+               , (GARDENING_TOOL, 100), (S_GRASS_STITCHER, 1) ]+  , iflavour = zipPlain [Green]+  , icount   = 1+  , irarity  = [(1, 1)] -- beyond level 3 they mostly appear with treePruner+  , iverbHit = "stab"+  , iweight  = 500+  , idamage  = 5 `d` 1+  , iaspects = [ Timeout 3  -- light and can hit with any side+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponFast+               , toVelocity 30 ]+  , ieffects = [gardenDestruct S_GRASS_STITCHER]+  , idesc    = ""  -- TODO: https://en.wikipedia.org/wiki/Grass_Stitcher+  , ikit     = [(GARDENING_TOOL, CGround), (GARDENING_TOOL, CGround)]+  }+ladiesFork = grassStitcher+  { iname    = "ladies' fork"+  , ifreq    = [ (COMMON_ITEM, 10), (HANDLE_AND_STEEL, 1)+               , (GARDENING_TOOL, 100), (S_LADIES_FORK, 1) ]+  , iflavour = zipFancy [Green]+  , iweight  = 1000+  , idamage  = 6 `d` 1+  , iaspects = [ Timeout 5+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponBig+               , toVelocity 40 ]+  , ieffects = [gardenDestruct S_LADIES_FORK]+  , idesc    = ""  -- TODO: https://en.wikipedia.org/wiki/Garden_fork+  , ikit     = [(GARDENING_TOOL, CGround)]+  }+hoe = grassStitcher+  { isymbol  = symbolHafted+  , iname    = "hoe"+  , ifreq    = [ (COMMON_ITEM, 10), (HANDLE_AND_STEEL, 1)+               , (GARDENING_TOOL, 100), (S_HOE, 1) ]+  , iflavour = zipFancy [Cyan]+  , iverbHit = "hack"+  , iweight  = 1000+  , idamage  = 7 `d` 1  -- neither sharp nor heavy+  , iaspects = [ Timeout 7+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponBig+               , toVelocity 40 ]+  , ieffects = [gardenDestruct S_HOE]+  , idesc    = ""  -- TODO: https://en.wikipedia.org/wiki/Hoe_(tool)+  , ikit     = [(GARDENING_TOOL, CGround)]+  }+spade = grassStitcher+  { isymbol  = symbolHafted  -- swinging much more deadly than gouging+  , iname    = "spade"+  , ifreq    = [ (COMMON_ITEM, 10), (HANDLE_AND_STEEL, 1)+               , (GARDENING_TOOL, 100), (S_SPADE, 1) ]+  , iflavour = zipPlain [Cyan]+  , iverbHit = "cut"+  , iweight  = 2000+  , idamage  = 8 `d` 1+  , iaspects = [ Timeout 9+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotWeaponBig+               , toVelocity 50 ]+  , ieffects = [gardenDestruct S_SPADE]+  , idesc    = ""  -- TODO: https://en.wikipedia.org/wiki/Spade+  , ikit     = []  -- most powerful, most likely to come alone+  }+treePruner = grassStitcher+  { iname    = "long reach tree pruner"+  , ifreq    = [(COMMON_ITEM, 100), (POLE_AND_STEEL, 1)]+  , iflavour = zipFancy [BrRed]+  , irarity  = [(1, 12)]  -- early, while the weapon is still useful+  , iweight  = 4500+  , idamage  = 3 `d` 1+  , iaspects = [ Timeout 7+               , AddSkill SkArmorMelee 20  -- sharp+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotArmorMelee+               , toVelocity 50 ]+  , ieffects = []+  , idesc    = "A heavy tree lopper on a sturdy long pole."+  , ikit     = [(GARDENING_TOOL, CGround)]+  }+cleaningPole = grassStitcher+  { iname    = "window cleaning pole"+  , ifreq    = [(COMMON_ITEM, 100), (POLE_AND_STEEL, 1)]+  , iflavour = zipPlain [Blue]+  , irarity  = [(10, 6)]  -- last chance of a long pole+  , iweight  = 3500+  , idamage  = 2 `d` 1+  , iaspects = [ AddSkill SkArmorMelee 10  -- not sharp, so weaker+               , SetFlag Durable, SetFlag Meleeable  -- a fence may melee with+               , EqpSlot EqpSlotArmorMelee+               , toVelocity 40 ]+  , ieffects = []+  , idesc    = "A cleaning contraption for glass surfaces, mounted on a long synthetic pole."+  , ikit     = []+  }+staff = grassStitcher+  { isymbol  = symbolHafted+  , iname    = "wooden staff"+  , ifreq    = [(HANDLE, 70), (POLE_OR_HANDLE, 55), (S_STAFF, 1)]+  , iflavour = zipPlain [Brown]+  , iverbHit = "prod"+  , iweight  = 1000+  , idamage  = 1 `d` 1+  , iaspects = [ SetFlag Durable  -- prevent AI wield; boring, often too weak+               , toVelocity 30 ]  -- a weak missile and that's all+  , ieffects = []+  , idesc    = "A handle of a make-shift tool to be crafted."+  , ikit     = []+  }+pipe = staff+  { iname    = "metal pipe"+  , ifreq    = [(HANDLE, 30), (POLE_OR_HANDLE, 15), (S_PIPE, 1)]+  , iflavour = zipFancy [BrBlue]+  , idamage  = 2 `d` 1+  , idesc    = "Around a meter long, light, strong and hard alloy pipe. With one or both ends cut diagonally and sharpened, this would become a formidable weapon."+  }+longPole = staff+  { iname    = "long pole"+  , ifreq    = [(POLE, 90), (POLE_OR_HANDLE, 30)]+  , iflavour = zipPlain [BrYellow]+  , iweight  = 3000+  , idamage  = 2 `d` 1+  , iaspects = [ AddSkill SkArmorMelee 10  -- not sharp, so weaker+               , SetFlag Durable, SetFlag Meleeable+               , EqpSlot EqpSlotArmorMelee+               , toVelocity 20 ]+  , idesc    = "Over two meters long, strong and light pole."+  }++-- ** Treasure++gemTemplate = ItemKind+  { isymbol  = symbolGold+  , iname    = "gem"+  , ifreq    = [(GEM_UNKNOWN, 1), (VALUABLE, 100)]+  , iflavour = zipPlain $ delete BrYellow brightCol  -- natural, so not fancy+  , icount   = 1+  , irarity  = []+  , iverbHit = "tap"+  , iweight  = 50+  , idamage  = 0+  , iaspects = [PresentAs GEM_UNKNOWN, SetFlag Precious]+  , ieffects = []+  , idesc    = "Precious, though useless. Worth around 100 gold grains."+  , ikit     = []+  }+gem1 = gemTemplate+  { ifreq    = [ (TREASURE, 100), (GEM, 100), (ANY_JEWELRY, 10)+               , (VALUABLE, 100) ]+  , irarity  = [(3, 0), (6, 12), (10, 8)]+  , iaspects = [AddSkill SkShine 1, AddSkill SkSpeed (-1)]+                 -- reflects strongly, distracts; so it glows in the dark,+                 -- is visible on dark floor, but not too tempting to wear+               ++ iaspects gemTemplate+  }+gem2 = gem1+  { ifreq    = [ (TREASURE, 150), (GEM, 100), (ANY_JEWELRY, 10)+               , (VALUABLE, 100) ]+  , irarity  = [(5, 0), (7, 25), (10, 8)]+  }+gem3 = gem1+  { ifreq    = [ (TREASURE, 150), (GEM, 100), (ANY_JEWELRY, 10)+               , (VALUABLE, 100) ]+  , irarity  = [(7, 0), (8, 30), (10, 8)]+  }+gem4 = gem1+  { ifreq    = [ (TREASURE, 150), (GEM, 100), (ANY_JEWELRY, 30)+               , (VALUABLE, 100) ]+  , irarity  = [(9, 0), (10, 40)]+  }+gem5 = gem1+  { isymbol  = symbolSpecial+  , iname    = "stimpack"+  , ifreq    = [ (TREASURE, 200), (GEM, 25), (ANY_JEWELRY, 10)+               , (VALUABLE, 100) ]+  , iflavour = zipPlain [BrYellow]+  , irarity  = [(1, 40), (10, 10)]+  , iaspects = [ ELabel "of youth", SetFlag Precious  -- not hidden+               , AddSkill SkOdor (-1) ]+  , ieffects = [RefillCalm 10, RefillHP 40]+  , idesc    = "Calms, heals, invigorates, rejuvenates and smells nice. No side-effects. As valuable as precious gems, at 100 gold grains each."+  }+currencyTemplate = ItemKind+  { isymbol  = symbolGold+  , iname    = "gold grain"+  , ifreq    = [(CURRENCY_UNKNOWN, 1), (VALUABLE, 1)]+  , iflavour = zipPlain [BrYellow]+  , icount   = 10 + 1 `d` 20 + 1 `dL` 20+  , irarity  = [(1, 25), (10, 10)]+  , iverbHit = "tap"+  , iweight  = 1+  , idamage  = 0+  , iaspects = [PresentAs CURRENCY_UNKNOWN, SetFlag Precious]+  , ieffects = []+  , idesc    = "Reliably valuable in every civilized place."+  , ikit     = []+  }+currency = currencyTemplate+  { ifreq    = [(TREASURE, 100), (S_CURRENCY, 100), (VALUABLE, 1)]+  , iaspects = [AddSkill SkShine 1, AddSkill SkSpeed (-1)]+               ++ iaspects currencyTemplate+  }++-- ** Tools to be actively used, but not worn++jumpingPole = ItemKind+  { isymbol  = symbolWand+  , iname    = "jumping pole"+  , ifreq    = [(COMMON_ITEM, 100), (POLE, 10)]+  , iflavour = zipFancy [White]+  , icount   = 1+  , irarity  = [(6, 14)]  -- effect useful early, but too many poles early+  , iverbHit = "prod"+  , iweight  = 4000+  , idamage  = 0+  , iaspects = [ Timeout $ (4 + 1 `d` 2 - 1 `dL` 2) * 5+               , SetFlag Durable ]+  , ieffects = [toOrganGood S_HASTED 1]+                 -- This works and doesn't cause AI loops. @InsertMove@+                 -- would produce an activation that doesn't change game state.+                 -- Hasting for an absolute number of turns would cause+                 -- an explosion of time when several poles are accumulated.+                 -- Here it speeds AI up for exactly the turn spent activating,+                 -- so when AI applies it repeatedly, it gets its time back and+                 -- is not stuck. In total, the exploration speed is unchanged,+                 -- but it's useful when fleeing in the dark to make distance+                 -- and when initiating combat, so it's OK that AI uses it.+                 -- Timeout is rather high, because for factions with leaders+                 -- some time is often gained, so this could be useful+                 -- even during melee, which would be tiresome to employ.+  , idesc    = "Makes you vulnerable at take-off, but then you are free like a bird."+  , ikit     = []+  }++-- ** Detachable and usable, in various ways, robot body parts++constructionHooter = necklaceTemplate+  { iname    = "construction hooter"+  , ifreq    = [(CONSTRUCTION_HOOTER, 1), (COMMON_ITEM, 1), (ARMOR_LOOSE, 1)]+                   -- extremely rare, but dropped by decontamination chambers+  , iflavour = zipPlain [BrRed]+  , irarity  = [(1, 1)]+  , iweight  = 1000+  , iaspects = [ AddSkill SkArmorMelee 2+               , SetFlag Durable, toVelocity 50+               , SetFlag Equipable, EqpSlot EqpSlotArmorMelee]+  , ieffects = [Yell, Summon CONSTRUCTION_ROBOT 1]+  , idesc    = "An emergency hooter for alarming human personnel in case their life is in danger. Worn by construction robots around their \"neck\", where it's least exposed, but even there it needs to be heavily armored and running on its own power supply."+  }+wasteContainer = ItemKind+  { isymbol  = symbolTool+  , iname    = "waste container"+  , ifreq    = [(WASTE_CONTAINER, 1), (WATER_SOURCE, 1), (ARMOR_LOOSE, 1)]+  , iflavour = zipLiquid [Green]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "spill over"+  , iweight  = 30000+  , idamage  = 0+  , iaspects = [ Timeout $ (1 `d` 2) * 30  -- robots should not summon too often+               , AddSkill SkArmorMelee 20  -- tempting+               , AddSkill SkMaxCalm (-30)  -- punishes excessive stacking+               , SetFlag Periodic, SetFlag Equipable+               , EqpSlot EqpSlotArmorMelee ]+  , ieffects = [ Detect DetectLoot 20+               , When (TriggeredBy ActivationPeriodic) $ SeqEffect+                   [ Summon MOBILE_ANIMAL $ 1 `dL` 2+                   , Explode S_WASTE ] ]+  , idesc    = "Waste recognition and utilization subsystem. Detects any stray item not registered as a passenger's cargo. Leaks a little. But one man's trash is another man's treasure and so this item has many beneficial uses."+  , ikit     = []+  }+spotlight = ItemKind+  { isymbol  = symbolLight+  , iname    = "spotlight"+  , ifreq    = [(SPOTLIGHT, 1)]+  , iflavour = zipPlain [White]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "illuminate"+  , iweight  = 3000+  , idamage  = 0+  , iaspects = [ AddSkill SkShine 3+               , AddSkill SkHurtMelee (-2)  -- heavy and unwieldy+               , SetFlag Equipable, EqpSlot EqpSlotShine ]+  , ieffects = [Detect DetectHidden 10]+  , idesc    = "Powerful wide-beam spotlight in an unwieldy rack-mounted package. On full power, it can shine through thin construction surfaces, underlying fault lines."+  , ikit     = []+  }+seeingItem = ItemKind+  { isymbol  = symbolRing+  , iname    = "visual sensor"+  , ifreq    = [(COMMON_ITEM, 100)]+  , iflavour = zipPlain [BrBlue]+  , icount   = 1+  , irarity  = [(1, 2)]+  , iverbHit = "gaze at"+  , iweight  = 500+  , idamage  = 0+  , iaspects = [ Timeout 3+               , AddSkill SkSight 10  -- a spyglass for quick wields+               , AddSkill SkMaxCalm 30  -- to diminish clipping sight by Calm+               , AddSkill SkShine 2  -- to lit corridors when flying+               , AddSkill SkMaxHP (-30)  -- prevent excessive stacking+               , SetFlag Periodic ]+  , ieffects = [ Detect DetectActor 20  -- rare enough that one-time is not OP+               , When (TriggeredBy ActivationPeriodic) $ SeqEffect+                   [ Explode S_SINGLE_SPARK+                   , toOrganNoTimer S_POISONED  -- really can't be worn+                   , Summon MOBILE_ROBOT 1 ] ]+  , idesc    = "An oversize visual sensor freshly torn out of some unfortunate robot. It still sends a clear picture to unidentified receivers, even though the coolant liquid seeps from the seized servos and many internal contacts spark loose."   , ikit     = []   }
GameDefinition/Content/ItemKindActor.hs view
@@ -1,33 +1,130 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Actor (or rather actor body trunk) definitions. module Content.ItemKindActor-  ( actors+  ( -- * Group name patterns+    pattern S_WOODEN_TORCH+  , pattern HERO, pattern SCOUT_HERO, pattern RANGER_HERO, pattern ESCAPIST_HERO, pattern AMBUSHER_HERO, pattern BRAWLER_HERO, pattern SOLDIER_HERO, pattern CIVILIAN, pattern MONSTER, pattern MOBILE_MONSTER, pattern SCOUT_MONSTER, pattern ANIMAL, pattern MOBILE_ANIMAL, pattern IMMOBILE_ANIMAL+  , pattern ADD_SIGHT, pattern ARMOR_RANGED, pattern ADD_NOCTO_1, pattern WEAK_ARROW, pattern LIGHT_ATTENUATOR, pattern FIREPROOF_CLOTH, pattern RING_OF_OPPORTUNITY_SNIPER, pattern ANY_ARROW, pattern STARTING_ARMOR, pattern STARTING_WEAPON, pattern GEM+  , pattern CRAWL_HERO, pattern MERCENARY_HERO, pattern AQUATIC_ANIMAL, pattern AQUATIC_MONSTER, pattern EXPLOSIVE_MONSTER, pattern ROBOT, pattern MOBILE_ROBOT, pattern IMMOBILE_ROBOT, pattern CONSTRUCTION_ROBOT+  , pattern S_BULLTEPROOF_VEST, pattern S_PERFUME_POTION, pattern S_EMPTY_FLASK+  , pattern COOKED_FOOD, pattern MERCENARY_WEAPON, pattern MERCENARY_AMMO, pattern RAW_MEAT_CHUNK, pattern ROASTED_MEAT_CHUNK, pattern NEEDLE, pattern CAN_OF_STICKY_FOAM, pattern TRANQUILIZER_DART, pattern WASTE_CONTAINER, pattern CONSTRUCTION_HOOTER, pattern SPOTLIGHT, pattern BLOWTORCH, pattern POLE, pattern POLE_OR_HANDLE, pattern BREACHING_TOOL, pattern BONDING_TOOL, pattern SHARPENING_TOOL, pattern WIRECUTTING_TOOL+  , actorsGN, actorsGNSingleton+  , -- * Content+    actors   ) where  import Prelude ()  import Game.LambdaHack.Core.Prelude +import Content.ItemKindOrgan import Game.LambdaHack.Content.ItemKind import Game.LambdaHack.Definition.Ability import Game.LambdaHack.Definition.Color import Game.LambdaHack.Definition.Defs import Game.LambdaHack.Definition.Flavour +-- * Group name patterns++actorsGNSingleton :: [GroupName ItemKind]+actorsGNSingleton =+       [S_WOODEN_TORCH, S_BULLTEPROOF_VEST, S_PERFUME_POTION, S_EMPTY_FLASK]++pattern S_WOODEN_TORCH, S_BULLTEPROOF_VEST, S_PERFUME_POTION, S_EMPTY_FLASK :: GroupName ItemKind++actorsGN :: [GroupName ItemKind]+actorsGN =+       [HERO, SCOUT_HERO, RANGER_HERO, ESCAPIST_HERO, AMBUSHER_HERO, BRAWLER_HERO, SOLDIER_HERO, CIVILIAN, MONSTER, MOBILE_MONSTER, SCOUT_MONSTER, ANIMAL, MOBILE_ANIMAL, IMMOBILE_ANIMAL]+    ++ [ADD_SIGHT, ARMOR_RANGED, ADD_NOCTO_1, WEAK_ARROW, LIGHT_ATTENUATOR, FIREPROOF_CLOTH, RING_OF_OPPORTUNITY_SNIPER, ANY_ARROW, STARTING_ARMOR, STARTING_WEAPON, GEM]+    ++ [CRAWL_HERO, MERCENARY_HERO, AQUATIC_ANIMAL, AQUATIC_MONSTER, EXPLOSIVE_MONSTER, ROBOT, MOBILE_ROBOT, IMMOBILE_ROBOT, CONSTRUCTION_ROBOT]+    ++ [COOKED_FOOD, MERCENARY_WEAPON, MERCENARY_AMMO, RAW_MEAT_CHUNK, ROASTED_MEAT_CHUNK, NEEDLE, CAN_OF_STICKY_FOAM, TRANQUILIZER_DART, WASTE_CONTAINER, CONSTRUCTION_HOOTER, SPOTLIGHT, BLOWTORCH, POLE, POLE_OR_HANDLE, BREACHING_TOOL, BONDING_TOOL, SHARPENING_TOOL, WIRECUTTING_TOOL]++pattern HERO, SCOUT_HERO, RANGER_HERO, ESCAPIST_HERO, AMBUSHER_HERO, BRAWLER_HERO, SOLDIER_HERO, CIVILIAN, MONSTER, MOBILE_MONSTER, SCOUT_MONSTER, ANIMAL, MOBILE_ANIMAL, IMMOBILE_ANIMAL :: GroupName ItemKind++pattern ADD_SIGHT, ARMOR_RANGED, ADD_NOCTO_1, WEAK_ARROW, LIGHT_ATTENUATOR, FIREPROOF_CLOTH, RING_OF_OPPORTUNITY_SNIPER, ANY_ARROW, STARTING_ARMOR, STARTING_WEAPON, GEM :: GroupName ItemKind++pattern CRAWL_HERO, MERCENARY_HERO, AQUATIC_ANIMAL, AQUATIC_MONSTER, EXPLOSIVE_MONSTER, ROBOT, MOBILE_ROBOT, IMMOBILE_ROBOT, CONSTRUCTION_ROBOT :: GroupName ItemKind++pattern COOKED_FOOD, MERCENARY_WEAPON, MERCENARY_AMMO, RAW_MEAT_CHUNK, ROASTED_MEAT_CHUNK, NEEDLE, CAN_OF_STICKY_FOAM, TRANQUILIZER_DART, WASTE_CONTAINER, CONSTRUCTION_HOOTER, SPOTLIGHT, BLOWTORCH, POLE, POLE_OR_HANDLE, BREACHING_TOOL, BONDING_TOOL, SHARPENING_TOOL, WIRECUTTING_TOOL :: GroupName ItemKind++-- ** Common+pattern HERO = GroupName "hero"+pattern SCOUT_HERO = GroupName "scout hero"+pattern RANGER_HERO = GroupName "ranger hero"+pattern ESCAPIST_HERO = GroupName "escapist hero"+pattern AMBUSHER_HERO = GroupName "ambusher hero"+pattern BRAWLER_HERO = GroupName "brawler hero"+pattern SOLDIER_HERO = GroupName "fighter hero"+pattern CIVILIAN = GroupName "civilian"+pattern MONSTER = GroupName "monster"+pattern MOBILE_MONSTER = GroupName "mobile monster"+pattern SCOUT_MONSTER = GroupName "scout monster"+pattern ANIMAL = GroupName "animal"+pattern MOBILE_ANIMAL = GroupName "mobile animal"+pattern IMMOBILE_ANIMAL = GroupName "immobile animal"++-- ** Allure-specific+pattern CRAWL_HERO = GroupName "crawl hero"+pattern MERCENARY_HERO = GroupName "mercenary hero"+pattern AQUATIC_ANIMAL = GroupName "aquatic animal"+pattern AQUATIC_MONSTER = GroupName "aquatic monster"+pattern EXPLOSIVE_MONSTER = GroupName "explosive monster"+pattern ROBOT = GroupName "robot"+pattern MOBILE_ROBOT = GroupName "mobile robot"+pattern IMMOBILE_ROBOT = GroupName "immobile robot"+pattern CONSTRUCTION_ROBOT = GroupName "construction robot"++-- ** Common+pattern S_WOODEN_TORCH = GroupName "wooden torch"++pattern ADD_SIGHT = GroupName "sight improvement"+pattern ARMOR_RANGED = GroupName "ranged armor"+pattern ADD_NOCTO_1 = GroupName "noctovision improvement"+pattern WEAK_ARROW = GroupName "weak arrow"+pattern LIGHT_ATTENUATOR = GroupName "light attenuator"+pattern FIREPROOF_CLOTH = GroupName "fireproof cloth"+pattern RING_OF_OPPORTUNITY_SNIPER = GroupName "ring of sniper"+pattern ANY_ARROW = GroupName "arrow"+pattern STARTING_ARMOR = GroupName "starting armor"+pattern STARTING_WEAPON = GroupName "starting weapon"+pattern GEM = GroupName "gem"++-- ** Allure-specific+pattern S_BULLTEPROOF_VEST = GroupName "bulletproof vest"+pattern S_PERFUME_POTION = GroupName "perfume potion"+pattern S_EMPTY_FLASK = GroupName "empty flask"++pattern COOKED_FOOD = GroupName "cooked food"+pattern MERCENARY_WEAPON = GroupName "mercenary weapon"+pattern MERCENARY_AMMO = GroupName "mercenary ammo"+pattern RAW_MEAT_CHUNK = GroupName "raw meat chunk"+pattern ROASTED_MEAT_CHUNK = GroupName "roasted meat chunk"+pattern NEEDLE = GroupName "needle"+pattern CAN_OF_STICKY_FOAM = GroupName "can of sticky foam"+pattern TRANQUILIZER_DART = GroupName "tranquillizer dart"+pattern WASTE_CONTAINER = GroupName "waste container"+pattern CONSTRUCTION_HOOTER = GroupName "construction hooter"+pattern SPOTLIGHT = GroupName "spotlight"+pattern BLOWTORCH = GroupName "blowtorch"+pattern POLE = GroupName "pole"+pattern POLE_OR_HANDLE = GroupName "pole or handle"+pattern BREACHING_TOOL = GroupName "breaching tool"+pattern BONDING_TOOL = GroupName "bonding tool"+pattern SHARPENING_TOOL = GroupName "sharpening tool"+pattern WIRECUTTING_TOOL = GroupName "wirecutting tool"++-- * Content+ actors :: [ItemKind] actors =-  [warrior, warrior2, warrior3, warrior4, warrior5, scout, ranger, escapist, ambusher, soldier, civilian, civilian2, civilian3, civilian4, civilian5, eye, fastEye, nose, elbow, torsor, goldenJackal, griffonVulture, skunk, armadillo, gilaMonster, rattlesnake, hyena, komodoDragon, alligator, rhinoceros, beeSwarm, hornetSwarm, thornbush]-  -- Allure-specific-  ++ [giantOctopus, razorwireFence, electricFence, activeFence, steamFaucet, biogasFaucet, medbotFaucet, surveillanceDrone, shepherdDrone, huntingDrone, homeRobot, wasteRobot, lightRobot, heavyRobot, weldedRobot, cleanerRobot]+  [warrior, warrior2, scout, ranger, escapist, ambusher, brawler, fighter, mercenary, civilian, civilian2, civilian3, civilian4, civilian5, eye, fastEye, nose, elbow, elbowTank, intruder, torsor, goldenJackal, griffonVulture, skunk, armadillo, gilaMonster, rattlesnake, hyena, komodoDragon, alligator, giantOctopus, lion, rhinoceros, beeSwarm, hornetSwarm, thornbush, razorwireFence, electricFence, activeFence, steamFaucet, coolingFaucet, medbotFaucet, dustFaucet, fuelFaucet, surveillanceDrone, shepherdDrone, huntingDrone, homeRobot, wasteRobot, lightRobot, heavyRobot, weldedRobot, cleanerRobot] -warrior,    warrior2, warrior3, warrior4, warrior5, scout, ranger, escapist, ambusher, soldier, civilian, civilian2, civilian3, civilian4, civilian5, eye, fastEye, nose, elbow, torsor, goldenJackal, griffonVulture, skunk, armadillo, gilaMonster, rattlesnake, hyena, komodoDragon, alligator, rhinoceros, beeSwarm, hornetSwarm, thornbush :: ItemKind--- Allure-specific-giantOctopus, razorwireFence, electricFence, activeFence, steamFaucet, biogasFaucet, medbotFaucet, surveillanceDrone, shepherdDrone, huntingDrone, homeRobot, wasteRobot, lightRobot, heavyRobot, weldedRobot, cleanerRobot :: ItemKind+warrior,    warrior2, scout, ranger, escapist, ambusher, brawler, fighter, mercenary, civilian, civilian2, civilian3, civilian4, civilian5, eye, fastEye, nose, elbow, elbowTank, intruder, torsor, goldenJackal, griffonVulture, skunk, armadillo, gilaMonster, rattlesnake, hyena, komodoDragon, alligator, giantOctopus, lion, rhinoceros, beeSwarm, hornetSwarm, thornbush, razorwireFence, electricFence, activeFence, steamFaucet, coolingFaucet, medbotFaucet, dustFaucet, fuelFaucet, surveillanceDrone, shepherdDrone, huntingDrone, homeRobot, wasteRobot, lightRobot, heavyRobot, weldedRobot, cleanerRobot :: ItemKind  -- Note that the actors that appear in the crawl scenario should -- be generated with at most ordinary ammo. Otherwise, farming them@@ -38,13 +135,22 @@ -- * Hunams  humanOrgans :: [(GroupName ItemKind, CStore)]-humanOrgans = [ ("fist", COrgan), ("foot", COrgan)-              , ("eye 6", COrgan), ("ear 3", COrgan)-              , ("sapient brain", COrgan) ]+humanOrgans = [ (S_FIST, COrgan), (S_FOOT, COrgan)+              , (S_EYE_6, COrgan), (S_EAR_3, COrgan)+              , (S_SAPIENT_BRAIN, COrgan)+              , (S_ANIMAL_STOMACH, COrgan), (S_HUNGRY, COrgan)+              , (BACKSTORY, COrgan) ]+                  -- TODO: when enough backstory items created,+                  -- include many backstory categories here instead and add+                  -- a bit of variation among warriors, some kinds+                  -- getting a good and a bad where another test a mixed one+                  -- instead, etc. Characters should still be recognizable+                  -- and slighty different backstory emphasis between games+                  -- matches human plasticity. warrior = ItemKind   { isymbol  = '@'-  , iname    = "mercenary"  -- modified if initial actors in hero faction-  , ifreq    = [("hero", 100), ("crawl hero", 100), ("mobile", 1)]+  , iname    = "adventurer"  -- modified if initial actors in hero faction+  , ifreq    = [(HERO, 100), (CRAWL_HERO, 100), (MOBILE, 1)]   , iflavour = zipPlain [BrWhite]   , icount   = 1   , irarity  = [(1, 5)]@@ -61,85 +167,86 @@                , AddSkill SkOdor 1                , SetFlag Durable ]   , ieffects = []-  , idesc    = ""-  , ikit     = humanOrgans ++ [("genetic flaw 10", COrgan)]+  , idesc    = "A human equipped for an adventure."+  , ikit     = humanOrgans+               ++ [ (GENETIC_FLAW_10, COrgan)+                  , (S_EMPTY_FLASK, CStash), (COMMON_ITEM, CStash) ]   } warrior2 = warrior-  { iname    = "pilot"-  -- , idesc    = ""-  }-warrior3 = warrior-  { iname    = "engineer"-  , ifreq    = [("crawl hero", 100), ("mobile", 1)]-  , ikit     = ikit warrior ++ [("currency", CSha)]-  -- , idesc    = ""-  }-warrior4 = warrior-  { iname    = "doctor"-  , ifreq    = [("crawl hero", 100), ("mobile", 1)]-  , ikit     = ikit warrior ++ [("currency", CSha)]-  -- , idesc    = ""-  }-warrior5 = warrior-  { iname    = "hacker"-  , ifreq    = [("crawl hero", 100), ("mobile", 1)]-  , ikit     = ikit warrior ++ [("currency", CSha)]-  -- , idesc    = ""+  { ifreq    = [(CRAWL_HERO, 100), (MOBILE, 1)]+  , ikit     = humanOrgans+               ++ [ (GENETIC_FLAW_10, COrgan), (S_CURRENCY, CStash)+                  , (COOKED_FOOD, CStash), (COMMON_ITEM, CStash) ]   }- scout = warrior   { iname    = "scout"-  , ifreq    = [("scout hero", 100), ("mobile", 1)]+  , ifreq    = [(SCOUT_HERO, 100), (MOBILE, 1)]   , ikit     = humanOrgans  -- no flaw-               ++ [ ("add sight", CEqp)-                  , ("armor ranged", CEqp)-                  , ("add nocto 1", CInv) ]-  -- , idesc    = ""+               ++ [ (ADD_SIGHT, CEqp)+                  , (ARMOR_RANGED, CEqp)+                  , (ADD_NOCTO_1, CStash) ]+  , idesc    = "A human equipped for scouting."   } ranger = warrior   { iname    = "ranger"-  , ifreq    = [("ranger hero", 100), ("mobile", 1)]+  , ifreq    = [(RANGER_HERO, 100), (MOBILE, 1)]   , ikit     = humanOrgans  -- no flaw-               ++ [ ("armor ranged", CEqp)-                  , ("weak arrow", CInv) ]-  -- , idesc    = ""+               ++ [ (ARMOR_RANGED, CEqp)+                  , (WEAK_ARROW, CStash) ]+  , idesc    = "A human equipped for ranged fight."   } escapist = warrior   { iname    = "escapist"-  , ifreq    = [("escapist hero", 100), ("mobile", 1)]+  , ifreq    = [(ESCAPIST_HERO, 100), (MOBILE, 1)]   , ikit     = humanOrgans  -- no flaw-               ++ [ ("add sight", CEqp)-                  , ("armor ranged", CEqp)-                  , ("weak arrow", CInv)  -- mostly for probing-                  , ("light source", CInv)-                  , ("wooden torch", CInv)-                  , ("blanket", CInv) ]-  -- , idesc    = ""+               ++ [ (ADD_SIGHT, CEqp)+                  , (STARTING_ARMOR, CEqp)+                  , (WEAK_ARROW, CStash)  -- mostly for probing+                  , (LIGHT_ATTENUATOR, CStash)+                  , (S_WOODEN_TORCH, CStash)+                  , (FIREPROOF_CLOTH, CStash) ]+  , idesc    = "A human equipped for an escape."   } ambusher = warrior   { iname    = "ambusher"-  , ifreq    = [("ambusher hero", 100), ("mobile", 1)]+  , ifreq    = [(AMBUSHER_HERO, 100), (MOBILE, 1)]   , ikit     = humanOrgans  -- dark and numerous, so more kit without exploring-               ++ [ ("ring of opportunity sniper", CEqp)-                  , ("any arrow", CSha)-                  , ("weak arrow", CInv)-                  , ("explosive", CSha)-                  , ("light source", CEqp)-                  , ("wooden torch", CInv) ]-  -- , idesc    = ""+               ++ [ (RING_OF_OPPORTUNITY_SNIPER, CEqp)+                  , (ANY_ARROW, CStash), (ANY_ARROW, CStash)+                  , (WEAK_ARROW, CStash)+                  , (EXPLOSIVE, CStash)+                  , (LIGHT_ATTENUATOR, CEqp)+                  , (S_WOODEN_TORCH, CStash) ]+  , idesc    = "A human equipped for an ambush."   }-soldier = warrior-  { iname    = "soldier"-  , ifreq    = [("soldier hero", 100), ("mobile", 1)]+brawler = warrior+  { iname    = "brawler"+  , ifreq    = [(BRAWLER_HERO, 100), (MOBILE, 1)]   , ikit     = humanOrgans  -- no flaw-               ++ [ ("starting weapon", CEqp)-                  , ("explosive", CSha) ]-  -- , idesc    = ""+               ++ [(STARTING_WEAPON, CEqp)]+  , idesc    = "A human equipped for melee fight."   }+fighter = brawler+  { iname    = "fighter"+  , ifreq    = [(SOLDIER_HERO, 100), (MOBILE, 1)]+  , ikit     = ikit brawler+               ++ [(STARTING_WEAPON, CEqp), (EXPLOSIVE, CStash)]+  , idesc    = "A human equipped for intense combat."+  }+mercenary = brawler+  { iname    = "mercenary"+  , ifreq    = [(MERCENARY_HERO, 100), (MOBILE, 1)]+  , ikit     = humanOrgans  -- no flaw+               ++ [ (MERCENARY_WEAPON, CEqp)+                  , (S_BULLTEPROOF_VEST, CEqp)+                  , (MERCENARY_AMMO, CStash)+                  , (EXPLOSIVE, CStash) ]+  , idesc    = "A professional security contractor."+  }  civilian = warrior   { iname    = "clerk"-  , ifreq    = [("civilian", 100), ("mobile", 1)]+  , ifreq    = [(CIVILIAN, 100), (MOBILE, 1)]   , iflavour = zipPlain [BrBlack]   -- , idesc    = ""   }@@ -164,11 +271,11 @@  -- They have bright colours, because they are not natural. -eye = ItemKind+eye = ItemKind  -- depends on items it finds rather than special organs   { isymbol  = 'w'   , iname    = "beckoning walker"-  , ifreq    = [ ("monster", 100), ("mobile", 1)-               , ("mobile monster", 100), ("scout monster", 10) ]+  , ifreq    = [ (MONSTER, 100), (MOBILE, 1)+               , (MOBILE_MONSTER, 100), (SCOUT_MONSTER, 10) ]   , iflavour = zipFancy [BrRed]   , icount   = 1   , irarity  = [(3 * 10/15, 0), (4 * 10/15, 10), (10, 8)]@@ -179,90 +286,139 @@                , AddSkill SkSpeed 20, AddSkill SkNocto 2                , AddSkill SkAggression 1                , AddSkill SkProject 2  -- can lob-               , AddSkill SkApply 1  -- can even use cultural artifacts+               , AddSkill SkApply 1  -- can use even cultural artifacts                , SetFlag Durable ]   , ieffects = []   , idesc    = "Walks with a stately dignity. You read death in the slow beckoning gestures of its revolting upper appendages."-  , ikit     = [ ("foot", COrgan), ("tentacle", COrgan)-               , ("bark", COrgan), ("eye 6", COrgan)-               , ("sapient brain", COrgan) ]  -- no voice, no hearing+  , ikit     = [ (S_FOOT, COrgan), (S_TENTACLE, COrgan)+               , (S_BARK, COrgan), (S_EYE_6, COrgan)+               , (S_SAPIENT_BRAIN, COrgan) ]  -- no voice, no hearing   }-fastEye = ItemKind+fastEye = ItemKind  -- glass cannon; depends mostly on items it finds   { isymbol  = 'b'   , iname    = "rolling biter"-  , ifreq    = [ ("monster", 100), ("mobile", 1)-               , ("mobile monster", 100), ("scout monster", 60) ]+  , ifreq    = [ (MONSTER, 100), (MOBILE, 1)+               , (MOBILE_MONSTER, 100), (SCOUT_MONSTER, 60) ]   , iflavour = zipFancy [BrBlue]   , icount   = 1   , irarity  = [(3 * 10/15, 0), (4 * 10/15, 3), (10, 12)]   , iverbHit = "thud"   , iweight  = 80000   , idamage  = 0-  , iaspects = [ AddSkill SkMaxHP 5, AddSkill SkMaxCalm 70+  , iaspects = [ AddSkill SkMaxHP 12, AddSkill SkMaxCalm 70                , AddSkill SkSpeed 30, AddSkill SkNocto 2+               , AddSkill SkHurtMelee 20                , AddSkill SkAggression 1                , SetFlag Durable ]   , ieffects = []-  , idesc    = "It bites as blindingly fast as it runs. Or rolls? Or crawls? Also, cuts and pierces."-  , ikit     = [ ("jaw", COrgan), ("razor", COrgan), ("horn", COrgan)-               , ("speed gland 10", COrgan)-               , ("eye 3", COrgan), ("ear 3", COrgan)-               , ("sapient brain", COrgan) ]+  , idesc    = "It bites as blindingly fast as it runs. Or rolls? Also, cuts and pierces."+  , ikit     = [ (S_JAW, COrgan), (S_RAZOR, COrgan), (S_HORN, COrgan)+               , (S_SMALL_CLAW, COrgan)  -- at least one non-timed, not deadly+               , (S_SPEED_GLAND_10, COrgan)  -- -30 armor with horn, +20 melee+               , (S_EYE_3, COrgan), (S_EAR_3, COrgan)+               , (S_SAPIENT_BRAIN, COrgan) ]   }-nose = ItemKind  -- depends solely on smell+nose = ItemKind  -- sniffs only; a tank requiring multiple weapon hits to beat;+                 -- no armor, so special kinds of damage don't help;+                 -- slow, but despite that, danger when strong weapons wielded!   { isymbol  = 'h'-  , iname    = "clawing horror"-  , ifreq    = [ ("monster", 100), ("mobile", 1), ("mobile monster", 100)-               , ("aquatic", 30), ("aquatic monster", 30) ]  -- likes liquids+  , iname    = "crawling horror"+  , ifreq    = [ (MONSTER, 100), (MOBILE, 1), (MOBILE_MONSTER, 100)+               , (AQUATIC, 30), (AQUATIC_MONSTER, 30) ]  -- likes liquids   , iflavour = zipFancy [BrGreen]   , icount   = 1   , irarity  = [(3 * 10/15, 0), (4 * 10/15, 5), (10, 9)]   , iverbHit = "thud"   , iweight  = 80000   , idamage  = 0-  , iaspects = [ AddSkill SkMaxHP 30, AddSkill SkMaxCalm 30-               , AddSkill SkSpeed 16, AddSkill SkNocto 2+  , iaspects = [ AddSkill SkMaxHP 60, AddSkill SkMaxCalm 30+               , AddSkill SkSpeed 15, AddSkill SkNocto 2                , AddSkill SkAggression 1                , AddSkill SkProject (-1)  -- can't project                , AddSkill SkSwimming 30                , SetFlag Durable ]   , ieffects = []-  , idesc    = "A blind, slimy mass of loose tissue. You'd think it's powerless, but as soon as it touches your trembling body, clawing, stinging and burning erupts and can't be fended off."-  , ikit     = [ ("small claw", COrgan), ("sting", COrgan)-               , ("venom tooth", COrgan)-               , ("nostril", COrgan)-               , ("sapient brain", COrgan) ]  -- no sight nor hearing+  , idesc    = "A blind, slimy mass of fluid tissue. You'd think it's powerless, but as soon as it touches your trembling body, slapping, stinging and burning, it won't let go."+  , ikit     = [ (S_STING, COrgan), (S_TIP, COrgan), (S_LIP, COrgan)+               , (S_NOSTRIL, COrgan), (S_HUNGRY, COrgan)+               , (S_SAPIENT_BRAIN, COrgan) ]  -- no sight nor hearing   } elbow = ItemKind   { isymbol  = 's'   , iname    = "creepy shooter"-  , ifreq    = [ ("monster", 100), ("mobile", 1)-               , ("mobile monster", 100), ("scout monster", 30) ]+  , ifreq    = [ (MONSTER, 100), (MOBILE, 1)+               , (MOBILE_MONSTER, 100), (SCOUT_MONSTER, 30) ]   , iflavour = zipFancy [BrMagenta]   , icount   = 1   , irarity  = [(3 * 10/15, 0), (4 * 10/15, 1), (10, 12)]   , iverbHit = "thud"   , iweight  = 80000   , idamage  = 0-  , iaspects = [ AddSkill SkMaxHP 12, AddSkill SkMaxCalm 80+  , iaspects = [ AddSkill SkMaxHP 12, AddSkill SkMaxCalm 100                , AddSkill SkSpeed 20, AddSkill SkNocto 2                , AddSkill SkProject 2  -- can lob-               , AddSkill SkApply 1  -- can even use cultural artifacts+               , AddSkill SkApply 1  -- can use even cultural artifacts                , AddSkill SkMelee (-1)                , SetFlag Durable ]   , ieffects = []   , idesc    = "It moves in sudden jerks and never makes a noise. Speaks in hard objects hurled at deadly speeds."-  , ikit     = [ ("speed gland 5", COrgan)-               , ("eye 6", COrgan), ("ear 8", COrgan)+  , ikit     = [ (S_SPEED_GLAND_5, COrgan)  -- -25 armor+               , (S_EYE_6, COrgan), (S_EAR_8, COrgan)                    -- too powerful to get stronger sight-               , ("sapient brain", COrgan)-               , ("any arrow", CSha), ("any arrow", CInv)-               , ("weak arrow", CInv), ("weak arrow", CInv) ]+               , (S_SAPIENT_BRAIN, COrgan)+               , (ANY_ARROW, CEqp), (ANY_ARROW, CStash)+               , (WEAK_ARROW, CEqp), (WEAK_ARROW, CStash) ]   }+elbowTank = elbow+  { iname    = "armored shooter"+  , iflavour = zipFancy [Magenta]+  , irarity  = [(10, 0), (40, 30)]  -- only appears among late spawns+  , ikit     = ikit elbow ++ [(S_ARMORED_SKIN, COrgan), (S_JET_BOOSTER, COrgan)]+  }++-- * Allure-specific aliens++-- TODO: the main fun in this actor will be chain explosions and for this,+-- it needs to spawn in groups and move in groups. Neither is implemented yet.+-- For now, we try to amass the actors on arena levels over water and in rooms.+-- Low HP is needed to ensure the chain reaction. Lack of ranged combat+-- makes the rule to attack it from a distance straightforward.+intruder = ItemKind+  { isymbol  = 'i'+  , iname    = "bobbing intruder"+  , ifreq    = [ (MONSTER, 100), (MOBILE, 1), (MOBILE_MONSTER, 100)+               , (EXPLOSIVE_MONSTER, 100)+               , (AQUATIC, 1) ]  -- neutral to water, unlike other actors+  , iflavour = zipFancy [BrCyan]+  , icount   = 1+  , irarity  = [(3 * 10/15, 0), (4 * 10/15, 5), (10, 9)]+  , iverbHit = "thud"+  , iweight  = 80000+  , idamage  = 0+  , iaspects = [ AddSkill SkMaxHP 6, AddSkill SkMaxCalm 50+               , AddSkill SkSpeed 20, AddSkill SkNocto 2+               , AddSkill SkAggression 1+               , AddSkill SkProject (-1)  -- can't project+               , AddSkill SkApply 1  -- can use even cultural artifacts+               , AddSkill SkAlter (-2)  -- can't use hard stairs nor doors+               , AddSkill SkFlying 10  -- flies slowly, but far+               , SetFlag Durable ]+  , ieffects = []+  , idesc    = "It starts, bobs and halts, scanning for movement. Effortlessly, it resumes covering the ground, gliding two meters above the floor. The pumped organic body is large, but looks fragile. However, it doesn't skirt the walls, but instead seems to take ownership of any space it boldly parks in the middle of."+  , ikit     = [ (S_FLOTATION_BAG, COrgan)+               , (S_TENTACLE, COrgan), (S_TENTACLE, COrgan)+               , (S_TIP, COrgan)  -- at least one non-timed+               , (S_HOOKED_CLAW, COrgan)+               , (S_EYE_6, COrgan), (S_EAR_6, COrgan)+               , (S_SAPIENT_BRAIN, COrgan) ]  -- no voice, no hearing+  }++-- * Alien uniques+ torsor = ItemKind   { isymbol  = 'M'-  , iname    = "Maker"-  , ifreq    = [("monster", 100), ("mobile", 1)]+  , iname    = "The Maker"+  , ifreq    = [(MONSTER, 100), (MOBILE, 1)]   , iflavour = zipFancy [BrCyan]   , icount   = 1   , irarity  = [(14 * 10/15, 0), (15 * 10/15, 1000)]  -- unique@@ -270,23 +426,26 @@   , iweight  = 80000   , idamage  = 0   , iaspects = [ SetFlag Unique, ELabel "of Contact"-               , AddSkill SkMaxHP 300, AddSkill SkMaxCalm 100-               , AddSkill SkSpeed 10, AddSkill SkNocto 2+               , AddSkill SkMaxHP 400  -- -50 melee armor, so higher HP+               , AddSkill SkMaxCalm 100+               , AddSkill SkSpeed 5+               , AddSkill SkNocto 2                , AddSkill SkAggression 3                , AddSkill SkProject 2  -- can lob-               , AddSkill SkApply 1  -- can even use cultural artifacts+               , AddSkill SkApply 1  -- can use even cultural artifacts                , AddSkill SkAlter (-1)  -- can't exit the gated level; a boss,                                         -- but can dig rubble, ice                , SetFlag Durable ]-  , ieffects = []+  , ieffects = [OnSmash $ VerbMsg "mount the last plea for mutual understanding and voluntary exchange of body parts" "."]   , idesc    = "The mind, the big heart behind it all. Warmth and sympathy pour out through the graceful undulation of tentacles, sharp claws, snapping jaw and dripping fangs."-  , ikit     = [ ("tentacle", COrgan), ("hooked claw", COrgan)-                   -- at least one non-timed-               , ("large jaw", COrgan), ("venom fang", COrgan)-               , ("speed gland 5", COrgan)-               , ("eye 6", COrgan), ("ear 8", COrgan)-               , ("sapient brain", COrgan)-               , ("gem", CInv), ("gem", CInv), ("gem", CInv), ("gem", CInv) ]+  , ikit     = [ (S_TENTACLE, COrgan), (S_HOOKED_CLAW, COrgan)+               , (S_LARGE_JAW, COrgan), (S_VENOM_FANG, COrgan)+               , (S_SMALL_CLAW, COrgan)  -- at least one non-timed, not deadly+               , (S_SPEED_GLAND_10, COrgan)+               , (S_EYE_6, COrgan), (S_EAR_8, COrgan)+               , (S_SAPIENT_BRAIN, COrgan)+               , (GEM, CStash), (GEM, CStash)+               , (GEM, CStash), (GEM, CStash) ]   }  -- * Animals@@ -294,11 +453,11 @@ -- They need rather strong melee, because they don't use items. -- They have dull colors, except for yellow, because there is no dull variant. -goldenJackal = ItemKind  -- basically a much smaller and slower hyena+goldenJackal = ItemKind  -- basically a much smaller, slower and nosy hyena   { isymbol  = 'j'   , iname    = "golden jackal"-  , ifreq    = [ ("animal", 100), ("mobile", 1), ("mobile animal", 100)-               , ("scavenger", 50) ]+  , ifreq    = [ (ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)+               , (SCAVENGER, 50) ]   , iflavour = zipPlain [BrYellow]   , icount   = 1   , irarity  = [(1, 4), (10, 2)]@@ -307,19 +466,22 @@   , idamage  = 0   , iaspects = [ AddSkill SkMaxHP 15, AddSkill SkMaxCalm 70                , AddSkill SkSpeed 24, AddSkill SkNocto 2+               , AddSkill SkAggression 2  -- scout+               , AddSkill SkDisplace 1  -- scout                , SetFlag Durable ]   , ieffects = []   , idesc    = "An opportunistic predator, feeding on carrion and the weak."-  , ikit     = [ ("small jaw", COrgan)-               , ("eye 6", COrgan), ("nostril", COrgan), ("ear 8", COrgan)-               , ("animal brain", COrgan)-               , ("genetic flaw 3", COrgan) ]+  , ikit     = [ (S_SMALL_JAW, COrgan)+               , (S_POWERFUL_HIND_LEGS, COrgan)  -- useful for aggressive actor+               , (S_EYE_6, COrgan), (S_NOSTRIL, COrgan), (S_EAR_8, COrgan)+               , (S_ANIMAL_BRAIN, COrgan)+               , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_3, COrgan) ]   }-griffonVulture = ItemKind+griffonVulture = ItemKind  -- keep it boring and weak, because it summons   { isymbol  = 'v'   , iname    = "griffon vulture"-  , ifreq    = [ ("animal", 100), ("mobile", 1), ("mobile animal", 100)-               , ("scavenger", 30) ]+  , ifreq    = [ (ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)+               , (SCAVENGER, 30) ]   , iflavour = zipPlain [BrYellow]   , icount   = 1   , irarity  = [(1, 3), (10, 3)]@@ -330,26 +492,25 @@                    -- enough Calm to summon twice only if not attacked at all;                    -- loses a lot of sight after summoning                , AddSkill SkSpeed 22, AddSkill SkNocto 2-               , AddSkill SkAlter (-2)  -- can't use normal stairs nor doors+               , AddSkill SkAlter (-2)  -- can't use hard stairs nor doors                , AddSkill SkFlying 10  -- flies slowly, but far                , SetFlag Durable ]       -- Animals don't have leader, usually, so even if only one on level,       -- it pays the communication overhead, so the speed is higher to get-      -- them on par with human leaders moving solo. Common random double moves,-      -- on either side, are just too bothersome.+      -- them on par with human leaders moving solo.   , ieffects = []   , idesc    = "It soars high above, searching for vulnerable prey."-  , ikit     = [ ("screeching beak", COrgan)  -- in reality it grunts and hisses-               , ("small claw", COrgan)-               , ("eye 8", COrgan), ("ear 8", COrgan)+  , ikit     = [ (S_SCREECHING_BEAK, COrgan)  -- in reality it grunts and hisses+               , (S_SMALL_CLAW, COrgan)+               , (S_EYE_8, COrgan), (S_EAR_8, COrgan)                    -- can't shoot, so strong sight is OK-               , ("animal brain", COrgan)-               , ("genetic flaw 3", COrgan) ]+               , (S_ANIMAL_BRAIN, COrgan)+               , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_3, COrgan) ]   } skunk = ItemKind   { isymbol  = 's'   , iname    = "hog-nosed skunk"-  , ifreq    = [("animal", 100), ("mobile", 1), ("mobile animal", 100)]+  , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [White]   , icount   = 1   , irarity  = [(1, 8), (5, 1)]@@ -358,43 +519,45 @@   , idamage  = 0   , iaspects = [ AddSkill SkMaxHP 13, AddSkill SkMaxCalm 30                , AddSkill SkSpeed 22, AddSkill SkNocto 2-               , AddSkill SkAlter (-2)  -- can't use normal stairs nor doors+               , AddSkill SkAlter (-2)  -- can't use hard stairs nor doors                , AddSkill SkOdor 5  -- and no smell skill, to let it leave smell                , SetFlag Durable ]   , ieffects = []   , idesc    = "Its only defence is the terrible stench."-  , ikit     = [ ("scent gland", COrgan)-               , ("small claw", COrgan), ("snout", COrgan)-               , ("eye 3", COrgan), ("ear 6", COrgan)-               , ("animal brain", COrgan)-               , ("genetic flaw 3", COrgan) ]+  , ikit     = [ (S_SCENT_GLAND, COrgan)+               , (S_SMALL_CLAW, COrgan), (S_SNOUT, COrgan)+               , (S_EYE_3, COrgan), (S_EAR_6, COrgan)+               , (S_ANIMAL_BRAIN, COrgan)+               , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_3, COrgan) ]   }-armadillo = ItemKind+armadillo = ItemKind  -- a tank with armor, so special damage defeats it   { isymbol  = 'a'   , iname    = "giant armadillo"-  , ifreq    = [("animal", 100), ("mobile", 1), ("mobile animal", 100)]+  , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [Brown]   , icount   = 1   , irarity  = [(1, 7)]   , iverbHit = "thud"-  , iweight  = 80000+  , iweight  = 54000   , idamage  = 0-  , iaspects = [ AddSkill SkMaxHP 13, AddSkill SkMaxCalm 30+  , iaspects = [ AddSkill SkMaxHP 30, AddSkill SkMaxCalm 30                , AddSkill SkSpeed 20, AddSkill SkNocto 2-               , AddSkill SkAlter (-2)  -- can't use normal stairs nor doors+               , AddSkill SkHurtMelee (-70)  -- quite harmless rolled in a ball+               , AddSkill SkAlter (-2)  -- can't use hard stairs nor doors                , SetFlag Durable ]   , ieffects = []   , idesc    = "When threatened, it rolls into a ball."-  , ikit     = [ ("hooked claw", COrgan), ("snout", COrgan)-               , ("armored skin", COrgan), ("armored skin", COrgan)-               , ("eye 3", COrgan), ("nostril", COrgan), ("ear 6", COrgan)-               , ("animal brain", COrgan)-               , ("genetic flaw 3", COrgan) ]+  , ikit     = [ (S_HOOKED_CLAW, COrgan), (S_SNOUT, COrgan)+               , (S_ARMORED_SKIN, COrgan), (S_ARMORED_SKIN, COrgan)+               , (S_EYE_3, COrgan), (S_NOSTRIL, COrgan), (S_EAR_6, COrgan)+               , (S_ANIMAL_BRAIN, COrgan)+               , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_3, COrgan)+               , (RAW_MEAT_CHUNK, CEqp) ]   } gilaMonster = ItemKind   { isymbol  = 'g'   , iname    = "Gila monster"-  , ifreq    = [("animal", 100), ("mobile", 1), ("mobile animal", 100)]+  , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [Magenta]   , icount   = 1   , irarity  = [(2, 5), (10, 2)]@@ -403,45 +566,49 @@   , idamage  = 0   , iaspects = [ AddSkill SkMaxHP 15, AddSkill SkMaxCalm 50                , AddSkill SkSpeed 18, AddSkill SkNocto 2-               , AddSkill SkAlter (-2)  -- can't use normal stairs nor doors+               , AddSkill SkAlter (-2)  -- can't use hard stairs nor doors                , SetFlag Durable ]   , ieffects = []   , idesc    = "Numbing venom ensures that even the fastest prey has no escape."-  , ikit     = [ ("venom tooth", COrgan), ("small claw", COrgan)-               , ("eye 3", COrgan), ("nostril", COrgan), ("ear 6", COrgan)-               , ("animal brain", COrgan)-               , ("genetic flaw 3", COrgan) ]+  , ikit     = [ (S_VENOM_TOOTH, COrgan), (S_SMALL_CLAW, COrgan)+               , (S_EYE_3, COrgan), (S_NOSTRIL, COrgan), (S_EAR_6, COrgan)+               , (S_ANIMAL_BRAIN, COrgan)  -- small reptile, hungers slowly+               , (GENETIC_FLAW_3, COrgan) ]   } rattlesnake = ItemKind   { isymbol  = 's'   , iname    = "rattlesnake"-  , ifreq    = [("animal", 100), ("mobile", 1), ("mobile animal", 100)]+  , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [Brown]   , icount   = 1-  , irarity  = [(5, 1), (10, 7)]+  , irarity  = [(5, 1), (10, 7), (20, 10)]  -- common among late spawns   , iverbHit = "thud"   , iweight  = 80000   , idamage  = 0   , iaspects = [ AddSkill SkMaxHP 28, AddSkill SkMaxCalm 60                , AddSkill SkSpeed 16, AddSkill SkNocto 2-               , AddSkill SkAlter (-2)  -- can't use normal stairs nor doors+               , AddSkill SkAggression 2  -- often discharged. so flees anyway+               , AddSkill SkAlter (-2)  -- can't use hard stairs nor doors                , SetFlag Durable ]   , ieffects = []   , idesc    = "Beware its rattle - it serves as a warning of an agonising death."-  , ikit     = [ ("venom fang", COrgan)  -- when on cooldown, it's weaponless-               , ("rattle", COrgan)-               , ("eye 3", COrgan), ("nostril", COrgan), ("ear 6", COrgan)-               , ("animal brain", COrgan)-               , ("genetic flaw 3", COrgan) ]+  , ikit     = [ (S_VENOM_FANG, COrgan)  -- when discharged, it's weaponless+               , (S_COILED_TAIL, COrgan)  -- useful for an aggressive actor+               , (S_RATLLE, COrgan)+               , (S_EYE_3, COrgan), (S_NOSTRIL, COrgan), (S_EAR_6, COrgan)+               , (S_ANIMAL_BRAIN, COrgan)  -- small reptile, hungers slowly+               , (GENETIC_FLAW_3, COrgan)+               , (RAW_MEAT_CHUNK, CEqp) ]   } hyena = ItemKind   { isymbol  = 'h'   , iname    = "spotted hyena"-  , ifreq    = [ ("animal", 100), ("mobile", 1), ("mobile animal", 100)-               , ("scavenger", 20) ]+  , ifreq    = [ (ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)+               , (SCAVENGER, 20) ]   , iflavour = zipPlain [BrYellow]   , icount   = 1-  , irarity  = [(4, 1), (10, 5)]  -- gets summoned often, so low base rarity+  , irarity  = [(4, 1), (10, 5), (20, 10)]+      -- gets summoned often, so low base rarity, except among late spawns   , iverbHit = "thud"   , iweight  = 60000   , idamage  = 0@@ -450,42 +617,47 @@                , SetFlag Durable ]   , ieffects = []   , idesc    = "Skulking in the shadows, waiting for easy prey."-  , ikit     = [ ("jaw", COrgan)-               , ("eye 6", COrgan), ("nostril", COrgan), ("ear 8", COrgan)-               , ("animal brain", COrgan)-               , ("genetic flaw 3", COrgan) ]+  , ikit     = [ (S_JAW, COrgan), (S_SMALL_CLAW, COrgan)+               , (S_EYE_6, COrgan), (S_NOSTRIL, COrgan), (S_EAR_8, COrgan)+               , (S_ANIMAL_BRAIN, COrgan)+               , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_3, COrgan) ]   } komodoDragon = ItemKind   { isymbol  = 'k'   , iname    = "Komodo dragon"-  , ifreq    = [("animal", 100), ("mobile", 1), ("mobile animal", 100)]+  , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [BrRed]  -- speedy, so bright red   , icount   = 1-  , irarity  = [(9, 0), (10, 11)]+  , irarity  = [(9, 0), (10, 11), (20, 20)]   , iverbHit = "thud"   , iweight  = 80000   , idamage  = 0-  , iaspects = [ AddSkill SkMaxHP 40, AddSkill SkMaxCalm 60+  , iaspects = [ AddSkill SkMaxHP 40, AddSkill SkMaxCalm 60  -- regens                , AddSkill SkSpeed 17, AddSkill SkNocto 2+               , AddSkill SkHurtMelee 60  -- great fighter with low cooldowns                , AddSkill SkAggression 1  -- match the description                , SetFlag Durable ]   , ieffects = []   , idesc    = "Larger and more aggressive than any other lizard, but as easily recovering from wounds as its lesser cousins."-  , ikit     = [ ("large tail", COrgan), ("jaw", COrgan)-               , ("hooked claw", COrgan)-               , ("speed gland 5", COrgan), ("armored skin", COrgan)-               , ("eye 3", COrgan), ("nostril", COrgan), ("ear 3", COrgan)-               , ("animal brain", COrgan)-               , ("genetic flaw 3", COrgan) ]  -- not to wake it up too soon+  , ikit     = [ (S_LARGE_TAIL, COrgan), (S_JAW, COrgan)+               , (S_LIP, COrgan), (S_FOOT, COrgan)+               , (S_SPEED_GLAND_5, COrgan), (S_ARMORED_SKIN, COrgan)+               , (S_EYE_3, COrgan), (S_NOSTRIL, COrgan), (S_EAR_3, COrgan)+               , (S_ANIMAL_BRAIN, COrgan)+               , (S_ANIMAL_STOMACH, COrgan)+               , (GENETIC_FLAW_3, COrgan)  -- not to wake it up too soon+               , (RAW_MEAT_CHUNK, CEqp), (RAW_MEAT_CHUNK, CEqp) ]   }-alligator = ItemKind+alligator = ItemKind  -- late, slow, deadly semi-tank with some armor;+                      -- too deadly to get more HP; bombs the only recourse   { isymbol  = 'a'   , iname    = "alligator"-  , ifreq    = [ ("animal", 100), ("mobile", 1), ("mobile animal", 100)-               , ("aquatic", 70), ("aquatic animal", 70) ]  -- amphibious+  , ifreq    = [ (ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)+               , (AQUATIC, 70), (AQUATIC_ANIMAL, 70) ]  -- amphibious   , iflavour = zipPlain [Blue]   , icount   = 1-  , irarity  = [(9, 0), (10, 10)]  -- extra spawns in water, so lower rarity+  , irarity  = [(9, 0), (10, 10), (20, 10), (40, 40)]+      -- extra spawns in water, so lower rarity, except among late spawns   , iverbHit = "thud"   , iweight  = 80000   , idamage  = 0@@ -497,36 +669,101 @@                , SetFlag Durable ]   , ieffects = []   , idesc    = "An armored predator from the dawn of time. You better not get within its reach."-  , ikit     = [ ("huge tail", COrgan), ("large jaw", COrgan)-               , ("small claw", COrgan)-               , ("armored skin", COrgan)-               , ("eye 6", COrgan), ("ear 8", COrgan)-               , ("animal brain", COrgan)-               , ("genetic flaw 10", COrgan) ]+  , ikit     = [ (S_HUGE_TAIL, COrgan)  -- the special trick, breaking frontline+               , (S_LARGE_JAW, COrgan)+               , (S_SMALL_CLAW, COrgan)+               , (S_ARMORED_SKIN, COrgan)+               , (S_EYE_6, COrgan), (S_EAR_8, COrgan)+               , (S_ANIMAL_BRAIN, COrgan)+               , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_10, COrgan)+               , (RAW_MEAT_CHUNK, CEqp), (RAW_MEAT_CHUNK, CEqp) ]   }-rhinoceros = ItemKind++-- * Allure-specific animals++giantOctopus = ItemKind+  { isymbol  = 'o'+  , iname    = "giant octopus"+  , ifreq    = [ (ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)+               , (AQUATIC, 90), (AQUATIC_ANIMAL, 90) ]  -- weak on land+  , iflavour = zipPlain [BrMagenta]  -- very exotic, so bright color+  , icount   = 1+  , irarity  = [(1, 3), (7, 3)]+  , iverbHit = "thud"+  , iweight  = 72000+  , idamage  = 0+  , iaspects = [ AddSkill SkMaxHP 17, AddSkill SkMaxCalm 80+               , AddSkill SkSwimming 100  -- swims better than walks+               , AddSkill SkSpeed 27, AddSkill SkNocto 3 -- good night vision+               , AddSkill SkAlter (-2)  -- can't use hard stairs nor doors+               , SetFlag Durable ]+  , ieffects = []+  , idesc    = "It has eight arms of rage and sees through the night. Copes with lower gravity better than most animals."  -- TODO: change when slowness on land is implemented+  , ikit     = [ (S_INK_SAC, COrgan)+               , (S_TENTACLE, COrgan), (S_TENTACLE, COrgan)+               , (S_TENTACLE, COrgan), (S_TENTACLE, COrgan)+               , (S_SMALL_BEAK, COrgan)  -- TODO: use when tentacles torn out+               , (S_EYE_8, COrgan)+                   -- shots not too damaging, so can have strong sight+               , (S_ANIMAL_BRAIN, COrgan)+               , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_3, COrgan)+               , (RAW_MEAT_CHUNK, CEqp), (RAW_MEAT_CHUNK, CEqp) ]+ }+lion = ItemKind  -- emphatically not a tank+  { isymbol  = 'l'+  , iname    = "Lion"+  , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]+  , iflavour = zipPlain [Red]+  , icount   = 1+  , irarity  = [(10, 0), (30, 50)]  -- only appears among late spawns+  , iverbHit = "thud"+  , iweight  = 140000+  , idamage  = 0+  , iaspects = [ AddSkill SkMaxHP 120, AddSkill SkMaxCalm 80+               , AddSkill SkSpeed 30, AddSkill SkNocto 2+               , AddSkill SkHurtMelee 60  -- great fighter+               , AddSkill SkAggression 2  -- late spawn+               , AddSkill SkApply 1  -- can eat food despite the genetic flaw+               , SetFlag Durable ]+  , ieffects = []+  , idesc    = "At the repeated violation of their pride area, the irritated felines emerge from hiding."+  , ikit     = [ (S_POWERFUL_HIND_LEGS, COrgan)+               , (S_LARGE_JAW, COrgan), (S_SMALL_CLAW, COrgan)+               , (S_EYE_6, COrgan), (S_EAR_6, COrgan)+               , (S_ANIMAL_BRAIN, COrgan)+               , (S_ANIMAL_STOMACH, COrgan), (GENETIC_FLAW_10, COrgan)+               , (RAW_MEAT_CHUNK, CEqp), (RAW_MEAT_CHUNK, CEqp) ]+  }++-- * Animal uniques++rhinoceros = ItemKind  -- impressive tank boss with some armor   { isymbol  = 'R'-  , iname    = "Maddened Rhinoceros"-  , ifreq    = [("animal", 100), ("mobile", 1)]+  , iname    = "Billy"+  , ifreq    = [(ANIMAL, 100), (MOBILE, 1)]   , iflavour = zipPlain [Brown]   , icount   = 1   , irarity  = [(7 * 10/15, 0), (8 * 10/15, 1000), (9 * 10/15, 0)]  -- unique   , iverbHit = "thud"   , iweight  = 80000   , idamage  = 0-  , iaspects = [ SetFlag Unique-               , AddSkill SkMaxHP 120, AddSkill SkMaxCalm 60+  , iaspects = [ SetFlag Unique, ELabel "the Maddened Rhinoceros"+               , AddSkill SkMaxHP 200, AddSkill SkMaxCalm 60                , AddSkill SkSpeed 27, AddSkill SkNocto 2+               , AddSkill SkHurtMelee 50  -- mass gives extra damage                , AddSkill SkAggression 2-               , AddSkill SkAlter (-1)  -- can't use normal stairs nor dig;-                                        -- a weak miniboss+               , AddSkill SkAlter (-1)  -- can't use hard stairs nor dig;+                                        -- a weak miniboss; can use easy stairs                , SetFlag Durable ]-  , ieffects = []+  , ieffects = [OnSmash $ VerbMsg "bellow triumphantly!" ""]   , idesc    = "The last of its kind. Blind with rage, or perhaps due to the postoperative scars. A huge mass of muscle that charges at deadly speed."-  , ikit     = [ ("rhino horn", COrgan), ("snout", COrgan)-               , ("armored skin", COrgan)-               , ("eye 3", COrgan), ("ear 8", COrgan)-               , ("animal brain", COrgan) ]+  , ikit     = [ (S_RHINO_HORN, COrgan), (S_FOOT, COrgan)+               , (S_RHINO_INERTIA, COrgan), (S_ARMORED_SKIN, COrgan)+               , (S_EYE_3, COrgan), (S_EAR_8, COrgan)+               , (S_ANIMAL_BRAIN, COrgan)+               , (S_ANIMAL_STOMACH, COrgan)+               , (RAW_MEAT_CHUNK, CEqp), (RAW_MEAT_CHUNK, CEqp)+               , (RAW_MEAT_CHUNK, CEqp), (RAW_MEAT_CHUNK, CEqp) ]   }  -- * Non-animal animals@@ -534,94 +771,68 @@ beeSwarm = ItemKind   { isymbol  = 'b'   , iname    = "bee swarm"-  , ifreq    = [("animal", 100), ("mobile", 1)]+  , ifreq    = [(ANIMAL, 100), (MOBILE, 1)]   , iflavour = zipPlain [Brown]   , icount   = 1   , irarity  = [(1, 3), (10, 4)]   , iverbHit = "buzz"   , iweight  = 1000   , idamage  = 0-  , iaspects = [ AddSkill SkMaxHP 8, AddSkill SkMaxCalm 60+  , iaspects = [ AddSkill SkMaxHP 10, AddSkill SkMaxCalm 60                , AddSkill SkSpeed 30, AddSkill SkNocto 2  -- armor in sting-               , AddSkill SkAlter (-2)  -- can't use normal stairs nor doors+               , AddSkill SkAlter (-2)  -- can't use hard stairs nor doors                , AddSkill SkWait (-2)  -- can't brace, sleep and lurk                , AddSkill SkFlying 10  -- flies slowly, but far                , SetFlag Durable ]   , ieffects = []   , idesc    = "Every bee would die for the queen."-  , ikit     = [ ("bee sting", COrgan)  -- weaponless when it's used up-               , ("vision 6", COrgan), ("ear 6", COrgan)-               , ("insect mortality", COrgan), ("animal brain", COrgan) ]+  , ikit     = [ (S_BEE_STING, COrgan)  -- weaponless when it's used up+               , (S_VISION_6, COrgan), (S_EAR_6, COrgan)+               , (S_INSECT_MORTALITY, COrgan), (S_ANIMAL_BRAIN, COrgan) ]   }-hornetSwarm = ItemKind+hornetSwarm = ItemKind  -- kind of tank with armor, but short-lived   { isymbol  = 'h'   , iname    = "hornet swarm"-  , ifreq    = [("animal", 100), ("mobile", 1), ("mobile animal", 100)]+  , ifreq    = [(ANIMAL, 100), (MOBILE, 1), (MOBILE_ANIMAL, 100)]   , iflavour = zipPlain [Magenta]   , icount   = 1-  , irarity  = [(5, 1), (10, 4)]  -- should be many, because die after a time+  , irarity  = [(5, 1), (10, 4), (20, 10)]+      -- should be many, because die after a time   , iverbHit = "buzz"   , iweight  = 1000   , idamage  = 0   , iaspects = [ AddSkill SkArmorMelee 80, AddSkill SkArmorRanged 40-               , AddSkill SkMaxHP 8, AddSkill SkMaxCalm 70+               , AddSkill SkHurtMelee 50+               , AddSkill SkMaxHP 10, AddSkill SkMaxCalm 70                , AddSkill SkSpeed 30, AddSkill SkNocto 2-               , AddSkill SkAlter (-2)  -- can't use normal stairs nor doors+               , AddSkill SkAlter (-2)  -- can't use hard stairs nor doors                , AddSkill SkWait (-2)  -- can't brace, sleep and lurk                , AddSkill SkFlying 10  -- flies slowly, but far                , SetFlag Durable ]   , ieffects = []   , idesc    = "A vicious cloud of stings and hate."-  , ikit     = [ ("sting", COrgan)  -- when on cooldown, it's weaponless-               , ("vision 6", COrgan), ("ear 6", COrgan)-               , ("insect mortality", COrgan), ("animal brain", COrgan) ]+  , ikit     = [ (S_STING, COrgan)  -- when on cooldown, it's weaponless+               , (S_VISION_6, COrgan), (S_EAR_6, COrgan)+               , (S_INSECT_MORTALITY, COrgan), (S_ANIMAL_BRAIN, COrgan) ]   }-thornbush = ItemKind+thornbush = ItemKind  -- the wimpiest kind of early tank   { isymbol  = 't'   , iname    = "thornbush"-  , ifreq    = [("animal", 30), ("immobile animal", 50)]+  , ifreq    = [(ANIMAL, 25), (IMMOBILE_ANIMAL, 40)]   , iflavour = zipPlain [Brown]   , icount   = 1   , irarity  = [(1, 13)]   , iverbHit = "scrape"   , iweight  = 80000   , idamage  = 0-  , iaspects = [ AddSkill SkMaxHP 20, AddSkill SkMaxCalm 999+  , iaspects = [ AddSkill SkMaxHP 30, AddSkill SkMaxCalm 999                , AddSkill SkSpeed 22, AddSkill SkNocto 2                , AddSkill SkWait 1, AddSkill SkMelee 1  -- no brain                , SetFlag Durable ]   , ieffects = []   , idesc    = "Each branch bears long, curved thorns."-  , ikit     = [ ("thorn", COrgan)  -- after all run out, it's weaponless-               , ("bark", COrgan) ]-  }---- * Allure-specific animals--giantOctopus = ItemKind-  { isymbol  = 'o'-  , iname    = "giant octopus"-  , ifreq    = [ ("animal", 100), ("mobile", 1), ("mobile animal", 100)-               , ("aquatic", 90), ("aquatic animal", 90) ]  -- weak on land-  , iflavour = zipPlain [BrMagenta]-  , icount   = 1-  , irarity  = [(1, 3)]-  , iverbHit = "thud"-  , iweight  = 72000-  , idamage  = 0-  , iaspects = [ AddSkill SkMaxHP 17, AddSkill SkMaxCalm 80-               , AddSkill SkSwimming 100  -- swims better than walks-               , AddSkill SkSpeed 27, AddSkill SkNocto 3 -- good night vision-               , AddSkill SkAlter (-2)  -- can't use normal stairs nor doors-               , SetFlag Durable ]-  , ieffects = []-  , idesc    = "It has eight arms of rage."-  , ikit     = [ ("tentacle", COrgan), ("tentacle", COrgan)-               , ("small beak", COrgan)  -- TODO: use when tentacles torn out-               , ("eye 8", COrgan)-                   -- shots not too damaging, so can have strong sight-               , ("animal brain", COrgan)-               , ("genetic flaw 3", COrgan) ]+  , ikit     = [ (S_THORN, COrgan)  -- after all run out, it's weaponless+               , (S_BARK, COrgan) ]   }  -- * Robots, Allure-specific@@ -632,7 +843,7 @@ razorwireFence = ItemKind   { isymbol  = 'f'   , iname    = "razorwire fence"-  , ifreq    = [("robot", 15), ("immobile robot", 10)]+  , ifreq    = [(ROBOT, 15), (IMMOBILE_ROBOT, 10)]   , iflavour = zipPlain [Cyan]   , icount   = 1   , irarity  = [(3 * 10/15, 0), (4 * 10/15, 20), (10, 4)]@@ -646,34 +857,35 @@                , SetFlag Durable ]   , ieffects = []   , idesc    = "Must have been bought by previous ship owners to contain the wild animal infestation."-  , ikit     = [("razor", COrgan), ("razor", COrgan)]+  , ikit     = [(S_RAZOR, COrgan), (S_THORN, COrgan)]   } electricFence = ItemKind   { isymbol  = 'f'   , iname    = "electric fence"-  , ifreq    = [("robot", 15), ("immobile robot", 10)]+  , ifreq    = [(ROBOT, 40), (IMMOBILE_ROBOT, 10)]   , iflavour = zipPlain [Blue]   , icount   = 1-  , irarity  = [(3 * 10/15, 0), (4 * 10/15, 15), (10, 5)]+  , irarity  = [(3 * 10/15, 0), (4 * 10/15, 10), (10, 10), (20, 10)]   , iverbHit = "thud"   , iweight  = 80000   , idamage  = 0-  , iaspects = [ AddSkill SkArmorMelee 30, AddSkill SkArmorRanged 15-               , AddSkill SkMaxHP 10, AddSkill SkMaxCalm 999-               , AddSkill SkSpeed 40, AddSkill SkNocto 2+  , iaspects = [ AddSkill SkMaxHP 30, AddSkill SkMaxCalm 999+                   -- no armor, because regenerates; high HP instead+               , AddSkill SkSpeed 40, AddSkill SkNocto 2, AddSkill SkShine 3                , AddSkill SkWait 1, AddSkill SkMelee 1  -- no brain                , SetFlag Durable ]   , ieffects = []-  , idesc    = "Marginally intelligent electric shepherd. Originally used in orbital dairy farms and planetary zoos."-  , ikit     = [("live wire", COrgan)]+  , idesc    = "Marginally intelligent electric shepherd. Originally used in orbital dairy farms and planetary zoos. The long support on which proximity sensors, actuators and wires are socketed, ensures animals can't jump above the fence, even in reduced gravity."+  , ikit     = [ (S_LIVE_WIRE, COrgan), (ELECTRIC_AMBIENCE, COrgan)+               , (POLE_OR_HANDLE, CEqp) ]   } activeFence = ItemKind   { isymbol  = 'f'   , iname    = "active fence"-  , ifreq    = [("robot", 27), ("immobile robot", 20)]+  , ifreq    = [(ROBOT, 30), (IMMOBILE_ROBOT, 20)]   , iflavour = zipPlain [BrMagenta]   , icount   = 1-  , irarity  = [(5 * 10/15, 0), (10, 7)]+  , irarity  = [(5 * 10/15, 0), (10, 7), (20, 10)]   , iverbHit = "thud"   , iweight  = 80000   , idamage  = 0@@ -685,8 +897,8 @@                , SetFlag Durable ]   , ieffects = []   , idesc    = "Makeshift, mostly non-lethal, autonomous perimeter defense outpost."-  , ikit     = [ ("vision 6", COrgan)-               , ("needle", CInv), ("can of sticky foam", CInv) ]+  , ikit     = [ (S_VISION_6, COrgan)+               , (NEEDLE, CStash), (CAN_OF_STICKY_FOAM, CStash) ]                    -- can of sticky foam is exploitable, but it spawns                    -- reasonably often only on one level and not for                    -- a long period@@ -694,43 +906,43 @@ steamFaucet = ItemKind   { isymbol  = 'f'   , iname    = "steam faucet"-  , ifreq    = [("robot", 8), ("immobile robot", 15)]-  , iflavour = zipPlain [BrBlue]+  , ifreq    = [(ROBOT, 8), (IMMOBILE_ROBOT, 15)]+  , iflavour = zipPlain [BrGreen]   , icount   = 1   , irarity  = [(1, 10), (10, 6)]   , iverbHit = "thud"   , iweight  = 80000   , idamage  = 0   , iaspects = [ AddSkill SkMaxHP 10, AddSkill SkMaxCalm 999-               , AddSkill SkSpeed 11, AddSkill SkNocto 2+               , AddSkill SkSpeed 7, AddSkill SkNocto 2                , AddSkill SkWait 1, AddSkill SkMelee 1  -- no brain                , SetFlag Durable ]   , ieffects = []   , idesc    = "A cracked valve on one of the superheated water pipes spreading radially outward from the tokamak level."-  , ikit     = [("boiling vent", COrgan), ("boiling fissure", COrgan)]+  , ikit     = [(S_BOILING_VENT, COrgan), (S_BOILING_FISSURE, COrgan)]   }-biogasFaucet = ItemKind+coolingFaucet = ItemKind   { isymbol  = 'f'-  , iname    = "biogas faucet"-  , ifreq    = [("robot", 8), ("immobile robot", 30)]-  , iflavour = zipPlain [BrGreen]+  , iname    = "cooling faucet"+  , ifreq    = [(ROBOT, 8), (IMMOBILE_ROBOT, 15)]+  , iflavour = zipPlain [BrBlue]   , icount   = 1   , irarity  = [(1, 10), (10, 6)]   , iverbHit = "thud"   , iweight  = 80000   , idamage  = 0   , iaspects = [ AddSkill SkMaxHP 20, AddSkill SkMaxCalm 999-               , AddSkill SkSpeed 22, AddSkill SkNocto 2, AddSkill SkShine 3+               , AddSkill SkSpeed 22, AddSkill SkNocto 2                , AddSkill SkWait 1, AddSkill SkMelee 1  -- no brain                , SetFlag Durable ]   , ieffects = []-  , idesc    = "An emergency pressure-release vent on a smelly biogas pipe."-  , ikit     = [("biogas vent", COrgan), ("biogas fissure", COrgan)]+  , idesc    = "An emergency pressure-release vent on a supercooling circuit reservoir."+  , ikit     = [(S_COOLING_VENT, COrgan), (S_COOLING_FISSURE, COrgan)]   } medbotFaucet = ItemKind   { isymbol  = 'f'   , iname    = "nano medbot faucet"-  , ifreq    = [("robot", 8), ("immobile robot", 100)]+  , ifreq    = [(ROBOT, 10), (IMMOBILE_ROBOT, 100)]   , iflavour = zipPlain [BrYellow]   , icount   = 1   , irarity  = [(1, 10), (10, 6)]@@ -743,12 +955,51 @@                , SetFlag Durable ]   , ieffects = []   , idesc    = "A faucet of a malfunctioning nano medical robot dispenser. Let's hope the medbots are still effective."-  , ikit     = [("medbot vent", COrgan), ("medbot fissure", COrgan)]+  , ikit     = [(S_MEDBOT_VENT, COrgan), (S_MEDBOT_FISSURE, COrgan)]   }+dustFaucet = ItemKind+  { isymbol  = 'f'+  , iname    = "dust faucet"+  , ifreq    = [ (ROBOT, 4)  -- usually nothing to ignite+               , (IMMOBILE_ROBOT, 30) ]  -- except when other faucets around+  , iflavour = zipPlain [BrCyan]+  , icount   = 1+  , irarity  = [(3, 20), (10, 6)]+  , iverbHit = "thud"+  , iweight  = 80000+  , idamage  = 0+  , iaspects = [ AddSkill SkMaxHP 10, AddSkill SkMaxCalm 999+               , AddSkill SkSpeed 11, AddSkill SkNocto 2+               , AddSkill SkWait 1, AddSkill SkMelee 1  -- no brain+               , SetFlag Durable ]+  , ieffects = []+  , idesc    = "A torn pipeline for venting flammable powders filtered from cargo areas out into the void, where they cannot ignite. Depending on the pressure in subsidiary ducts, it may contain dust of aluminum, magnesium, titanium, flour, starch, various nitrates and perchlorates."+  , ikit     = [(S_DUST_VENT, COrgan), (S_DUST_FISSURE, COrgan)]+  }+fuelFaucet = ItemKind+  { isymbol  = 'f'+  , iname    = "burning fuel faucet"+  , ifreq    = [(ROBOT, 30)]+      -- common not in outermost level, but in the dungeon, because it's+      -- self-contained, providing both fuel and ignition+  , iflavour = zipPlain [BrRed]+  , icount   = 1+  , irarity  = [(3 * 10/15, 0), (4 * 10/15, 6), (10, 12), (20, 10)]+  , iverbHit = "thud"+  , iweight  = 80000+  , idamage  = 0+  , iaspects = [ AddSkill SkMaxHP 10, AddSkill SkMaxCalm 999+               , AddSkill SkSpeed 11, AddSkill SkNocto 2+               , AddSkill SkWait 1, AddSkill SkMelee 1  -- no brain+               , SetFlag Durable ]+  , ieffects = []+  , idesc    = "Fuel station gone wrong. Multiple emergency subsystems added over the years, owing to valiant regulatory lawmaking efforts, keep it from exploding by turning off and on the vent and each other in a complex cyclical pattern."+  , ikit     = [(S_FUEL_VENT, COrgan), (S_FUEL_FISSURE, COrgan)]+  } surveillanceDrone = ItemKind   { isymbol  = 'd'   , iname    = "surveillance drone"-  , ifreq    = [("robot", 100), ("mobile", 100), ("mobile robot", 100)]+  , ifreq    = [(ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)]   , iflavour = zipPlain [Blue]   , icount   = 1   , irarity  = [(1, 3)]@@ -757,21 +1008,21 @@   , idamage  = 0   , iaspects = [ AddSkill SkArmorMelee 30, AddSkill SkArmorRanged 15                , AddSkill SkMaxHP 6, AddSkill SkMaxCalm 90-               , AddSkill SkSpeed 30, AddSkill SkNocto 2-               , AddSkill SkDisplace (-1)  -- as dumb as an animal-               , AddSkill SkMoveItem (-1)+               , AddSkill SkSpeed 25, AddSkill SkNocto 2+               , AddSkill SkMoveItem (-1)  -- almost as dumb as an animal                , AddSkill SkProject (-1)                , AddSkill SkMelee (-1)                , SetFlag Durable ]   , ieffects = []-  , idesc    = "A video camera in each room would violate privacy of passengers, hence surveillance drones. Programmed to be easy to fend off, they keep a respectful distance."-  , ikit     = [ ("vision 16", COrgan), ("robot brain", COrgan) ]+  , idesc    = "A video camera in each room would violate privacy of passengers, hence surveillance drones. Programmed to be easy to fend off, they try to keep a respectful distance, even at the cost to themselves."+  , ikit     = [ (S_JET_BOOSTER, COrgan)+               , (S_VISION_16, COrgan), (S_ROBOT_BRAIN, COrgan) ]   } shepherdDrone = ItemKind   { isymbol  = 'd'   , iname    = "oversight drone"-  , ifreq    = [ ("robot", 100), ("mobile", 100), ("mobile robot", 100)-               , ("construction robot", 100) ]+  , ifreq    = [ (ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)+               , (CONSTRUCTION_ROBOT, 100) ]   , iflavour = zipPlain [BrRed]   , icount   = 1   , irarity  = [(1, 3), (10, 4)]  -- gets summoned often, so low base rarity@@ -779,47 +1030,47 @@   , iweight  = 1000   , idamage  = 0   , iaspects = [ AddSkill SkArmorMelee 80, AddSkill SkArmorRanged 40-               , AddSkill SkMaxHP 3, AddSkill SkMaxCalm 60-               , AddSkill SkSpeed 30, AddSkill SkNocto 2-               , AddSkill SkAlter (-1)  -- can't open doors; roams open spaces-               , AddSkill SkDisplace (-1)  -- as dumb as an animal-               , AddSkill SkMoveItem (-1)+               , AddSkill SkMaxHP 10, AddSkill SkMaxCalm 60+               , AddSkill SkSpeed 25, AddSkill SkNocto 2+               , AddSkill SkAggression 2  -- scout+               , AddSkill SkMoveItem (-1)  -- almost as dumb as an animal                , AddSkill SkProject (-1)                , SetFlag Durable ]   , ieffects = []-  , idesc    = "An tiny airborne robot designed to take construction measurements, synchronize robot workers and report irregularities, if any."-  , ikit     = [ ("live wire", COrgan), ("vision 16", COrgan), ("ear 8", COrgan)-               , ("robot brain", COrgan) ]+  , idesc    = "A tiny airborne robot designed to take construction measurements, synchronize robot workers and report irregularities. It seems to be in need of resetting itself."+  , ikit     = [ (S_JET_BOOSTER, COrgan), (S_LIVE_WIRE, COrgan)+               , (S_VISION_16, COrgan), (S_EAR_8, COrgan)+               , (S_ROBOT_BRAIN, COrgan) ]   } huntingDrone = ItemKind   { isymbol  = 'd'   , iname    = "hunting drone"-  , ifreq    = [("robot", 100), ("mobile", 100), ("mobile robot", 100)]+  , ifreq    = [(ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)]   , iflavour = zipPlain [Green]   , icount   = 1-  , irarity  = [(4, 0), (5, 5), (10, 8)]+  , irarity  = [(4, 0), (5, 5), (10, 8), (20, 10)]   , iverbHit = "clank"   , iweight  = 500   , idamage  = 0   , iaspects = [ AddSkill SkArmorMelee 30, AddSkill SkArmorRanged 15-               , AddSkill SkMaxHP 6, AddSkill SkMaxCalm 60-               , AddSkill SkSpeed 40, AddSkill SkNocto 2-               , AddSkill SkDisplace (-1)  -- almost as dumb as an animal-               , AddSkill SkMoveItem (-1)  -- but can project-               , AddSkill SkMelee (-1)+               , AddSkill SkMaxHP 10, AddSkill SkMaxCalm 60+               , AddSkill SkSpeed 30, AddSkill SkNocto 2+               , AddSkill SkMoveItem (-1)  -- almost as dumb as an animal+               , AddSkill SkMelee (-1)  -- but can project                , SetFlag Durable ]   , ieffects = []-  , idesc    = "Originally designed for hunting down and putting to sleep stray animals. The sleeping agent must have long since dried up."-  , ikit     = [ ("eye 8", COrgan), ("nostril", COrgan), ("ear 8", COrgan)+  , idesc    = "Originally designed for hunting down and putting to sleep stray animals. The sleeping agent has long since dried up and the propulsion got rather unpredictable."+  , ikit     = [ (S_JET_BOOSTER, COrgan)+               , (S_EYE_8, COrgan), (S_NOSTRIL, COrgan), (S_EAR_8, COrgan)                    -- week projectiles, so strong sight OK-               , ("robot brain", COrgan)-               , ("needle", CInv), ("tranquillizer dart", CInv) ]+               , (S_ROBOT_BRAIN, COrgan)+               , (NEEDLE, CStash), (TRANQUILIZER_DART, CStash) ]   } homeRobot = ItemKind   { isymbol  = 'r'   , iname    = "feral home robot"                -- TODO: name another 'deranged', tertiary imperative: survival-  , ifreq    = [("robot", 100), ("mobile", 100), ("mobile robot", 100)]+  , ifreq    = [(ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)]   , iflavour = zipPlain [Magenta]   , icount   = 1   , irarity  = [(1, 15), (10, 5)]@@ -832,36 +1083,36 @@                , SetFlag Durable ]   , ieffects = []   , idesc    = "Once a timid household robot, now sufficiently adapted to survive in the deadly environment."-  , ikit     = [ ("fist", COrgan)-               , ("eye 3", COrgan), ("nostril", COrgan), ("ear 3", COrgan)-               , ("robot brain", COrgan) ]+  , ikit     = [ (S_FIST, COrgan)+               , (S_EYE_3, COrgan), (S_NOSTRIL, COrgan), (S_EAR_3, COrgan)+               , (S_ROBOT_BRAIN, COrgan) ]   }-wasteRobot = ItemKind+wasteRobot = ItemKind  -- not a tank, because smell-only alien is already a tank   { isymbol  = 'r'   , iname    = "waste disposal robot"-  , ifreq    = [ ("robot", 100), ("mobile", 100), ("mobile robot", 100)-               , ("construction robot", 100) ]+  , ifreq    = [ (ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)+               , (CONSTRUCTION_ROBOT, 100) ]   , iflavour = zipPlain [Green]   , icount   = 1   , irarity  = [(5, 9)]  -- gets summoned often, so low base rarity   , iverbHit = "clank"   , iweight  = 80000   , idamage  = 0-  , iaspects = [ AddSkill SkMaxHP 17, AddSkill SkMaxCalm 30+  , iaspects = [ AddSkill SkMaxHP 20, AddSkill SkMaxCalm 30                , AddSkill SkSpeed 15, AddSkill SkNocto 2                , SetFlag Durable ]   , ieffects = []-  , idesc    = "You are not in its database, hence you are waste."-  , ikit     = [ ("tentacle", COrgan)-               , ("nostril", COrgan)  -- only smell, for variety-               , ("robot brain", COrgan)-               , ("waste container", CEqp) ]+  , idesc    = "You are not in its database, hence you are waste. It can't see and you smell funny, so that must suffice."+  , ikit     = [ (S_TENTACLE, COrgan), (S_SNOUT, COrgan)+               , (S_NOSTRIL, COrgan)  -- only smell, for variety+               , (S_ROBOT_BRAIN, COrgan)+               , (WASTE_CONTAINER, CEqp) ]   } lightRobot = ItemKind   { isymbol  = 'r'   , iname    = "decoration robot"-  , ifreq    = [ ("robot", 100), ("mobile", 100), ("mobile robot", 100)-               , ("construction robot", 100) ]+  , ifreq    = [ (ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)+               , (CONSTRUCTION_ROBOT, 100) ]   , iflavour = zipPlain [BrYellow]   , icount   = 1   , irarity  = [(3 * 10/15, 0), (4 * 10/15, 6), (10, 6)]@@ -879,24 +1130,25 @@                , SetFlag Durable ]   , ieffects = []   , idesc    = "Interior and exterior decoration robot. Strongly fancies deep reds recently."-  , ikit     = [ ("hooked claw", COrgan), ("tentacle", COrgan)-               , ("hull plating", COrgan)-               , ("eye 6", COrgan), ("ear 8", COrgan)-               , ("robot brain", COrgan)-               , ("construction hooter", CEqp) ]+  , ikit     = [ (S_HOOKED_CLAW, COrgan), (S_TENTACLE, COrgan), (S_FOOT, COrgan)+               , (S_HULL_PLATING, COrgan)+               , (S_EYE_6, COrgan), (S_EAR_8, COrgan)+               , (S_ROBOT_BRAIN, COrgan)+               , (CONSTRUCTION_HOOTER, CEqp) ]   }-heavyRobot = ItemKind+heavyRobot = ItemKind  -- summoning tank with armor, but fortunately weak+                       -- weapons; danger when strong weapons wielded!   { isymbol  = 'r'   , iname    = "demolition robot"-  , ifreq    = [ ("robot", 100), ("mobile", 100), ("mobile robot", 100)-               , ("construction robot", 70) ]+  , ifreq    = [ (ROBOT, 100), (MOBILE, 100), (MOBILE_ROBOT, 100)+               , (CONSTRUCTION_ROBOT, 70) ]   , iflavour = zipPlain [Cyan]   , icount   = 1-  , irarity  = [(7 * 10/15, 0), (8 * 10/15, 4), (10, 13)]+  , irarity  = [(7 * 10/15, 0), (8 * 10/15, 4), (10, 13), (30, 30)]   , iverbHit = "clank"   , iweight  = 800000   , idamage  = 0-  , iaspects = [ AddSkill SkMaxHP 41, AddSkill SkMaxCalm 40+  , iaspects = [ AddSkill SkMaxHP 60, AddSkill SkMaxCalm 40                    -- can't summon again for a long time;                    -- loses a lot of sight after summoning                , AddSkill SkSpeed 20, AddSkill SkNocto 2@@ -906,62 +1158,78 @@                , SetFlag Durable ]   , ieffects = []   , idesc    = "Heavy multi-purpose construction robot. Excels at discharging, dismantling and demolition."-  , ikit     = [ ("large jaw", COrgan), ("small claw", COrgan)-               , ("hull plating", COrgan)-               , ("eye 3", COrgan), ("ear 6", COrgan)-               , ("robot brain", COrgan)-               , ("spotlight", CEqp), ("construction hooter", CEqp) ]+  , ikit     = [ (S_HORN, COrgan), (S_FIST, COrgan), (S_SMALL_CLAW, COrgan)+               , (S_HULL_PLATING, COrgan)+               , (S_EYE_3, COrgan), (S_EAR_6, COrgan)+               , (S_ROBOT_BRAIN, COrgan)+               , (SPOTLIGHT, CEqp), (CONSTRUCTION_HOOTER, CEqp) ]   }++-- * Robot uniques, Allure-specific+ weldedRobot = ItemKind-  { isymbol  = 'W'-  , iname    = "Smiling Welded Robot"-  , ifreq    = [("robot", 100), ("immobile robot", 100)]+  { isymbol  = 'L'+  , iname    = "Bob"+  , ifreq    = [(ROBOT, 100), (IMMOBILE_ROBOT, 100)]   , iflavour = zipPlain [BrCyan]   , icount   = 1   , irarity  = [(1 * 10/15, 1000), (2 * 10/15, 0)]  -- unique   , iverbHit = "clank"   , iweight  = 80000   , idamage  = 0-  , iaspects = [ SetFlag Unique+  , iaspects = [ SetFlag Unique, ELabel "the Welded Luggage Robot"                , AddSkill SkMaxHP 200, AddSkill SkMaxCalm 100                , AddSkill SkSpeed 20, AddSkill SkNocto 2                , SetFlag Durable ]-  , ieffects = []-  , idesc    = "A well-built humanoid luggage unloading robot with a smooth satin silvery skin. Its graceful moves are stunted by a thick irregular weld fastening both its shapely legs to the floor. A whiff of smoke escapes whenever it opens its mouth in a charming toothy smile while brandishing a blowtorch in its trembling hand."-  , ikit     = [ ("fist", COrgan)-               , ("eye 6", COrgan), ("ear 3", COrgan)-               , ("mouth vent", COrgan)-               , ("robot brain", COrgan)-               , ("crude weld", COrgan), ("blowtorch", CEqp) ]+  , ieffects = [OnSmash $ VerbMsg "lament dying with disfigured and welded legs that nobody wanted to fix with the blowtorch" "."]+  , idesc    = "A well-built humanoid luggage unloading robot with a smooth silvery satin skin. Its graceful moves are stunted by a thick irregular weld fastening both its shapely legs to the floor. A whiff of smoke escapes whenever it opens its mouth in a charming toothy smile while brandishing a blowtorch in its trembling hand. Blowtorch! That's the key to open the welded staircase."+  , ikit     = [ (S_FIST, COrgan)+               , (S_EYE_6, COrgan), (S_EAR_3, COrgan)+               , (S_MOUTH_VENT, COrgan)+               , (S_ROBOT_BRAIN, COrgan)+               , (S_CRUDE_WELD, COrgan)+               , (S_CURRENCY, CGround) -- to ensure newbies know to visit+               , (BLOWTORCH, CEqp)+               , (S_PERFUME_POTION, CStash), (WIRECUTTING_TOOL, CStash) ]+                   -- establish stash to ensure heroes pick up blowtorch ASAP   } cleanerRobot = ItemKind   { isymbol  = 'C'-  , iname    = "Void Cleaner Robot"-  , ifreq    = [("robot", 100), ("mobile", 1)]+  , iname    = "The Void Cleaner Robot"+  , ifreq    = [(ROBOT, 100), (MOBILE, 1)]   , iflavour = zipPlain [BrGreen]   , icount   = 1-  , irarity  = [(11 * 10/15, 0), (12 * 10/15, 1000), (13 * 10/15, 0)]-                 -- unique, appears at 11 of 12+  , irarity  = [(11 * 10/15, 0), (12 * 10/15, 1000), (13 * 10/15, 0)]  -- unique   , iverbHit = "clank"   , iweight  = 800000   , idamage  = 0   , iaspects = [ SetFlag Unique-               , AddSkill SkMaxHP 120, AddSkill SkMaxCalm 40+               , AddSkill SkMaxHP 120+                   -- doubly regenerates and huge armor (72), so lower HP+               , AddSkill SkMaxCalm 40                    -- can't summon again for a long time;                    -- loses a lot of sight after summoning                , AddSkill SkSpeed 18, AddSkill SkNocto 2                , AddSkill SkAggression 1-                   -- can't use normal stairs nor dig; a weak miniboss+                   -- can't use hard stairs nor dig; a weak miniboss;+                   -- however, it can use the easy stairs and so change levels                , AddSkill SkApply 1  -- can apply the hooter                , SetFlag Durable ]-  , ieffects = []+  , ieffects = [OnSmash $ VerbMsg "clumsily try to pick up a dust speck" "."]   , idesc    = "An oversize waste disposal robot repaired with parts from a demolition robot, including a scaled up goal matrix. The cosmic void is now the only acceptable model of cleanliness. The robot's bulky trunk doesn't fit into even the larger lift carriages."-  , ikit     = [ ("live wire", COrgan), ("large jaw", COrgan)-               , ("tentacle", COrgan)-               , ("boiling vent", COrgan), ("hull plating", COrgan)-               , ("eye 3", COrgan), ("nostril", COrgan), ("ear 6", COrgan)-               , ("robot brain", COrgan)-               , ("currency", CInv), ("currency", CInv), ("currency", CInv)-               , ("waste container", CEqp), ("spotlight", CEqp)-               , ("construction hooter", CEqp) ]+  , ikit     = [ (S_TENTACLE, COrgan), (S_SNOUT, COrgan)+               , (S_HORN, COrgan), (S_SMALL_CLAW, COrgan)  -- no fist+               , (S_LIVE_WIRE, COrgan)  -- patched from parts+               , (ELECTRIC_AMBIENCE, COrgan)  -- regeneration+               , (S_BOILING_VENT, COrgan)  -- regeneration+               , (S_HULL_PLATING, COrgan)+                   -- the only such armor, except for weak animals; plus+                   -- the WASTE_CONTAINER, so only hammers and proj effective+               , (S_EYE_3, COrgan), (S_NOSTRIL, COrgan), (S_EAR_6, COrgan)+               , (S_ROBOT_BRAIN, COrgan)+               , (S_CURRENCY, CStash)+               , (S_CURRENCY, CStash)+               , (S_CURRENCY, CStash)+               , (WASTE_CONTAINER, CEqp), (SPOTLIGHT, CEqp)+               , (CONSTRUCTION_HOOTER, CEqp) ]   }
GameDefinition/Content/ItemKindBlast.hs view
@@ -1,18 +1,28 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Blast definitions. module Content.ItemKindBlast-  ( blasts+  ( -- * Group name patterns+    pattern S_FIRECRACKER, pattern S_VIOLENT_FRAGMENTATION, pattern S_FRAGMENTATION, pattern S_FOCUSED_FRAGMENTATION, pattern S_VIOLENT_CONCUSSION, pattern S_CONCUSSION, pattern S_FOCUSED_CONCUSSION, pattern S_VIOLENT_FLASH, pattern S_FOCUSED_FLASH, pattern S_GLASS_HAIL, pattern S_FOCUSED_GLASS_HAIL, pattern S_PHEROMONE, pattern S_CALMING_MIST, pattern S_DISTRESSING_ODOR, pattern S_HEALING_MIST, pattern S_HEALING_MIST_2, pattern S_WOUNDING_MIST, pattern S_DISTORTION, pattern S_SMOKE, pattern S_BOILING_WATER, pattern S_GLUE, pattern S_WASTE, pattern S_ANTI_SLOW_MIST, pattern S_ANTIDOTE_MIST, pattern S_SLEEP_MIST, pattern S_DENSE_SHOWER, pattern S_SPARSE_SHOWER, pattern S_MELEE_PROTECTIVE_BALM, pattern S_RANGE_PROTECTIVE_BALM, pattern S_DEFENSELESSNESS_RUNOUT, pattern S_RESOLUTION_DUST, pattern S_HASTE_SPRAY, pattern S_EYE_DROP, pattern S_IRON_FILING, pattern S_SMELLY_DROPLET, pattern S_EYE_SHINE, pattern S_WHISKEY_SPRAY, pattern S_YOUTH_SPRINKLE, pattern S_POISON_CLOUD, pattern S_PING_PLASH, pattern S_VIOLENT_BURNING_OIL_2, pattern S_VIOLENT_BURNING_OIL_3, pattern S_VIOLENT_BURNING_OIL_4, pattern S_BURNING_OIL_2, pattern S_BURNING_OIL_3, pattern S_BURNING_OIL_4, pattern S_FOCUSED_BURNING_OIL_2, pattern S_FOCUSED_BURNING_OIL_3, pattern S_FOCUSED_BURNING_OIL_4+  , blastNoStatOf, blastBonusStatOf+  , pattern ARMOR_MISC+  , pattern S_VIOLENT_SLOWNESS_MIST, pattern S_SLOWNESS_MIST, pattern S_FOCUSED_SLOWNESS_MIST, pattern S_PAINT_DROPLET, pattern S_RHINO_HOLOGRAM, pattern S_CURRENT_DISCHARGE, pattern S_CURRENT_RECHARGE+  , pattern ADVERTISEMENT, pattern STORY_TELLING+  , pattern FIRE_SOURCE, pattern OIL_SOURCE, pattern WATER_SOURCE, pattern COLD_SOURCE, pattern BLAST_SOURCE+  , blastsGNSingleton, blastsGN+  , -- * Content+    blasts   ) where  import Prelude ()  import Game.LambdaHack.Core.Prelude +import Content.ItemKindTemporary import Game.LambdaHack.Content.ItemKind import Game.LambdaHack.Core.Dice import Game.LambdaHack.Definition.Ability@@ -20,15 +30,122 @@ import Game.LambdaHack.Definition.Defs import Game.LambdaHack.Definition.Flavour +-- * Group name patterns++blastsGNSingleton :: [GroupName ItemKind]+blastsGNSingleton =+       [S_FIRECRACKER, S_VIOLENT_FRAGMENTATION, S_FRAGMENTATION, S_FOCUSED_FRAGMENTATION, S_VIOLENT_CONCUSSION, S_CONCUSSION, S_FOCUSED_CONCUSSION, S_VIOLENT_FLASH, S_FOCUSED_FLASH, S_GLASS_HAIL, S_FOCUSED_GLASS_HAIL, S_PHEROMONE, S_CALMING_MIST, S_DISTRESSING_ODOR, S_HEALING_MIST, S_HEALING_MIST_2, S_WOUNDING_MIST, S_DISTORTION, S_SMOKE, S_BOILING_WATER, S_GLUE, S_WASTE, S_ANTI_SLOW_MIST, S_ANTIDOTE_MIST, S_SLEEP_MIST, S_DENSE_SHOWER, S_SPARSE_SHOWER, S_MELEE_PROTECTIVE_BALM, S_RANGE_PROTECTIVE_BALM, S_DEFENSELESSNESS_RUNOUT, S_RESOLUTION_DUST, S_HASTE_SPRAY, S_EYE_DROP, S_IRON_FILING, S_SMELLY_DROPLET, S_EYE_SHINE, S_WHISKEY_SPRAY, S_YOUTH_SPRINKLE, S_POISON_CLOUD, S_PING_PLASH, S_VIOLENT_BURNING_OIL_2, S_VIOLENT_BURNING_OIL_3, S_VIOLENT_BURNING_OIL_4, S_BURNING_OIL_2, S_BURNING_OIL_3, S_BURNING_OIL_4, S_FOCUSED_BURNING_OIL_2, S_FOCUSED_BURNING_OIL_3, S_FOCUSED_BURNING_OIL_4]+  ++ map firecrackerAt [1..4]+  ++ map blastNoStatOf noStatGN+  ++ map blastBonusStatOf bonusStatGN+  ++ [S_VIOLENT_SLOWNESS_MIST, S_SLOWNESS_MIST, S_FOCUSED_SLOWNESS_MIST, S_PAINT_DROPLET, S_RHINO_HOLOGRAM, S_CURRENT_DISCHARGE, S_CURRENT_RECHARGE]++pattern S_FIRECRACKER, S_VIOLENT_FRAGMENTATION, S_FRAGMENTATION, S_FOCUSED_FRAGMENTATION, S_VIOLENT_CONCUSSION, S_CONCUSSION, S_FOCUSED_CONCUSSION, S_VIOLENT_FLASH, S_FOCUSED_FLASH, S_GLASS_HAIL, S_FOCUSED_GLASS_HAIL, S_PHEROMONE, S_CALMING_MIST, S_DISTRESSING_ODOR, S_HEALING_MIST, S_HEALING_MIST_2, S_WOUNDING_MIST, S_DISTORTION, S_SMOKE, S_BOILING_WATER, S_GLUE, S_WASTE, S_ANTI_SLOW_MIST, S_ANTIDOTE_MIST, S_SLEEP_MIST, S_DENSE_SHOWER, S_SPARSE_SHOWER, S_MELEE_PROTECTIVE_BALM, S_RANGE_PROTECTIVE_BALM, S_DEFENSELESSNESS_RUNOUT, S_RESOLUTION_DUST, S_HASTE_SPRAY, S_EYE_DROP, S_IRON_FILING, S_SMELLY_DROPLET, S_EYE_SHINE, S_WHISKEY_SPRAY, S_YOUTH_SPRINKLE, S_POISON_CLOUD, S_PING_PLASH, S_VIOLENT_BURNING_OIL_2, S_VIOLENT_BURNING_OIL_3, S_VIOLENT_BURNING_OIL_4, S_BURNING_OIL_2, S_BURNING_OIL_3, S_BURNING_OIL_4, S_FOCUSED_BURNING_OIL_2, S_FOCUSED_BURNING_OIL_3, S_FOCUSED_BURNING_OIL_4 :: GroupName ItemKind++pattern S_VIOLENT_SLOWNESS_MIST, S_SLOWNESS_MIST, S_FOCUSED_SLOWNESS_MIST, S_PAINT_DROPLET, S_RHINO_HOLOGRAM, S_CURRENT_DISCHARGE, S_CURRENT_RECHARGE :: GroupName ItemKind++blastsGN :: [GroupName ItemKind]+blastsGN =+       [ARMOR_MISC]+    ++ [ADVERTISEMENT, STORY_TELLING]+    ++ [FIRE_SOURCE, OIL_SOURCE, WATER_SOURCE, COLD_SOURCE, BLAST_SOURCE]++pattern ARMOR_MISC :: GroupName ItemKind++pattern ADVERTISEMENT, STORY_TELLING :: GroupName ItemKind++pattern FIRE_SOURCE, OIL_SOURCE, WATER_SOURCE, COLD_SOURCE, BLAST_SOURCE :: GroupName ItemKind++pattern S_FIRECRACKER = GroupName "firecracker"+pattern S_VIOLENT_FRAGMENTATION = GroupName "violent fragmentation"+pattern S_FRAGMENTATION = GroupName "fragmentation"+pattern S_FOCUSED_FRAGMENTATION = GroupName "focused fragmentation"+pattern S_VIOLENT_CONCUSSION = GroupName "violent concussion"+pattern S_CONCUSSION = GroupName "concussion"+pattern S_FOCUSED_CONCUSSION = GroupName "focused concussion"+pattern S_VIOLENT_FLASH = GroupName "violent flash"+pattern S_FOCUSED_FLASH = GroupName "focused flash"+pattern S_GLASS_HAIL = GroupName "glass hail"+pattern S_FOCUSED_GLASS_HAIL = GroupName "focused glass hail"+pattern S_PHEROMONE = GroupName "pheromone"+pattern S_CALMING_MIST = GroupName "calming mist"+pattern S_DISTRESSING_ODOR = GroupName "distressing odor"+pattern S_HEALING_MIST = GroupName "healing mist"+pattern S_HEALING_MIST_2 = GroupName "strong healing mist"+pattern S_WOUNDING_MIST = GroupName "wounding mist"+pattern S_DISTORTION = GroupName "distortion"+pattern S_SMOKE = GroupName "smoke"+pattern S_BOILING_WATER = GroupName "boiling water"+pattern S_GLUE = GroupName "glue"+pattern S_WASTE = GroupName "waste"+pattern S_ANTI_SLOW_MIST = GroupName "anti-slow mist"+pattern S_ANTIDOTE_MIST = GroupName "antidote mist"+pattern S_SLEEP_MIST = GroupName "sleep mist"+pattern S_DENSE_SHOWER = GroupName "dense shower"+pattern S_SPARSE_SHOWER = GroupName "sparse shower"+pattern S_MELEE_PROTECTIVE_BALM = GroupName "melee protective balm"+pattern S_RANGE_PROTECTIVE_BALM = GroupName "ranged protective balm"+pattern S_DEFENSELESSNESS_RUNOUT = GroupName "acid spray"+pattern S_RESOLUTION_DUST = GroupName "resolution dust"+pattern S_HASTE_SPRAY = GroupName "haste spray"+pattern S_EYE_DROP = GroupName "eye drop"+pattern S_IRON_FILING = GroupName "iron filing"+pattern S_SMELLY_DROPLET = GroupName "smelly droplet"+pattern S_EYE_SHINE = GroupName "eye shine"+pattern S_WHISKEY_SPRAY = GroupName "whiskey spray"+pattern S_YOUTH_SPRINKLE = GroupName "youth sprinkle"+pattern S_POISON_CLOUD = GroupName "poison cloud"+pattern S_PING_PLASH = GroupName "ping and flash"+pattern S_VIOLENT_BURNING_OIL_2 = GroupName "violent burning oil 2"+pattern S_VIOLENT_BURNING_OIL_3 = GroupName "violent burning oil 3"+pattern S_VIOLENT_BURNING_OIL_4 = GroupName "violent burning oil 4"+pattern S_BURNING_OIL_2 = GroupName "burning oil 2"+pattern S_BURNING_OIL_3 = GroupName "burning oil 3"+pattern S_BURNING_OIL_4 = GroupName "burning oil 4"+pattern S_FOCUSED_BURNING_OIL_2 = GroupName "focused burning oil 2"+pattern S_FOCUSED_BURNING_OIL_3 = GroupName "focused burning oil 3"+pattern S_FOCUSED_BURNING_OIL_4 = GroupName "focused burning oil 4"++firecrackerAt :: Int -> GroupName ItemKind+firecrackerAt n = GroupName $ "firecracker" <+> tshow n++blastNoStatOf :: GroupName ItemKind -> GroupName ItemKind+blastNoStatOf grp = GroupName $ fromGroupName grp <+> "mist"++blastBonusStatOf :: GroupName ItemKind -> GroupName ItemKind+blastBonusStatOf grp = GroupName $ fromGroupName grp <+> "dew"++pattern ARMOR_MISC = GroupName "miscellaneous armor"++-- ** Allure-specific+pattern S_VIOLENT_SLOWNESS_MIST = GroupName "violent nitrogen mist"+pattern S_SLOWNESS_MIST = GroupName "nitrogen mist"+pattern S_FOCUSED_SLOWNESS_MIST = GroupName "focused nitrogen mist"+pattern S_PAINT_DROPLET = GroupName "paint droplet"+pattern S_RHINO_HOLOGRAM = GroupName "rhino hologram"+pattern S_CURRENT_DISCHARGE = GroupName "discharge current"+pattern S_CURRENT_RECHARGE = GroupName "recharge current"++pattern ADVERTISEMENT = GroupName "advertisement"+pattern STORY_TELLING = GroupName "story-telling piece"++pattern FIRE_SOURCE = GroupName "fire source"+pattern OIL_SOURCE = GroupName "oil source"+pattern WATER_SOURCE = GroupName "water source"+pattern COLD_SOURCE = GroupName "cold source"+pattern BLAST_SOURCE = GroupName "blast source"++-- * Content+ blasts :: [ItemKind] blasts =-  [burningOil2, burningOil3, burningOil4, firecracker1, firecracker2, firecracker3, firecracker4, firecracker5, spreadFragmentation, spreadFragmentation8, focusedFragmentation, spreadConcussion, spreadConcussion8, focusedConcussion, spreadFlash, spreadFlash8, focusedFlash, singleSpark, glassPiece, focusedGlass, fragrance, pheromone, mistCalming, odorDistressing, mistHealing, mistHealing2, mistWounding, distortion, smoke, boilingWater, glue, waste, mistAntiSlow, mistAntidote, mistSleep, denseShower, sparseShower, protectingBalmMelee, protectingBalmRanged, vulnerabilityBalm, resolutionDust, hasteSpray, slownessMist, eyeDrop, ironFiling, smellyDroplet, eyeShine, whiskeySpray, youthSprinkle, poisonCloud, blastNoSkMove, blastNoSkMelee, blastNoSkDisplace, blastNoSkAlter, blastNoSkWait, blastNoSkMoveItem, blastNoSkProject, blastNoSkApply, blastBonusSkMove, blastBonusSkMelee, blastBonusSkDisplace, blastBonusSkAlter, blastBonusSkWait, blastBonusSkMoveItem, blastBonusSkProject, blastBonusSkApply]+  [spreadBurningOil2, spreadBurningOil3, spreadBurningOil4, spreadBurningOil82, spreadBurningOil83, spreadBurningOil84, focusedBurningOil2, focusedBurningOil3, focusedBurningOil4, firecracker1, firecracker2, firecracker3, firecracker4, firecracker5, spreadFragmentation, spreadFragmentation8, focusedFragmentation, spreadConcussion, spreadConcussion8, focusedConcussion, spreadFlash, spreadFlash8, focusedFlash, singleSpark, glassPiece, focusedGlass, fragrance, pheromone, mistCalming, odorDistressing, mistHealing, mistHealing2, mistWounding, distortion, smoke, boilingWater, glue, waste, mistAntiSlow, mistAntidote, mistSleep, denseShower, sparseShower, protectingBalmMelee, protectingBalmRanged, defenselessnessRunout, resolutionDust, hasteSpray, eyeDrop, ironFiling, smellyDroplet, eyeShine, whiskeySpray, youthSprinkle, poisonCloud, pingFlash, blastNoSkMove, blastNoSkMelee, blastNoSkDisplace, blastNoSkAlter, blastNoSkWait, blastNoSkMoveItem, blastNoSkProject, blastNoSkApply, blastBonusSkMove, blastBonusSkMelee, blastBonusSkDisplace, blastBonusSkAlter, blastBonusSkWait, blastBonusSkMoveItem, blastBonusSkProject, blastBonusSkApply]   -- Allure-specific-  ++ [cruiseAdHologram, outerAdHologram, victoriaClassHologram, allureIntroHologram]+  ++ [cruiseAdHologram, outerAdHologram, victoriaClassHologram, allureIntroHologram, spreadNitrogen, spreadNitrogen8, focusedNitrogen, paintSpray, currentDischarge, currentRecharge] -burningOil2,    burningOil3, burningOil4, firecracker1, firecracker2, firecracker3, firecracker4, firecracker5, spreadFragmentation, spreadFragmentation8, focusedFragmentation, spreadConcussion, spreadConcussion8, focusedConcussion, spreadFlash, spreadFlash8, focusedFlash, singleSpark, glassPiece, focusedGlass, fragrance, pheromone, mistCalming, odorDistressing, mistHealing, mistHealing2, mistWounding, distortion, smoke, boilingWater, glue, waste, mistAntiSlow, mistAntidote, mistSleep, denseShower, sparseShower, protectingBalmMelee, protectingBalmRanged, vulnerabilityBalm, resolutionDust, hasteSpray, slownessMist, eyeDrop, ironFiling, smellyDroplet, eyeShine, whiskeySpray, youthSprinkle, poisonCloud, blastNoSkMove, blastNoSkMelee, blastNoSkDisplace, blastNoSkAlter, blastNoSkWait, blastNoSkMoveItem, blastNoSkProject, blastNoSkApply, blastBonusSkMove, blastBonusSkMelee, blastBonusSkDisplace, blastBonusSkAlter, blastBonusSkWait, blastBonusSkMoveItem, blastBonusSkProject, blastBonusSkApply :: ItemKind+spreadBurningOil2,    spreadBurningOil3, spreadBurningOil4, spreadBurningOil82, spreadBurningOil83, spreadBurningOil84, focusedBurningOil2, focusedBurningOil3, focusedBurningOil4, firecracker1, firecracker2, firecracker3, firecracker4, firecracker5, spreadFragmentation, spreadFragmentation8, focusedFragmentation, spreadConcussion, spreadConcussion8, focusedConcussion, spreadFlash, spreadFlash8, focusedFlash, singleSpark, glassPiece, focusedGlass, fragrance, pheromone, mistCalming, odorDistressing, mistHealing, mistHealing2, mistWounding, distortion, smoke, boilingWater, glue, waste, mistAntiSlow, mistAntidote, mistSleep, denseShower, sparseShower, protectingBalmMelee, protectingBalmRanged, defenselessnessRunout, resolutionDust, hasteSpray, eyeDrop, ironFiling, smellyDroplet, eyeShine, whiskeySpray, youthSprinkle, poisonCloud, pingFlash, blastNoSkMove, blastNoSkMelee, blastNoSkDisplace, blastNoSkAlter, blastNoSkWait, blastNoSkMoveItem, blastNoSkProject, blastNoSkApply, blastBonusSkMove, blastBonusSkMelee, blastBonusSkDisplace, blastBonusSkAlter, blastBonusSkWait, blastBonusSkMoveItem, blastBonusSkProject, blastBonusSkApply :: ItemKind -- Allure-specific-cruiseAdHologram,       outerAdHologram, victoriaClassHologram, allureIntroHologram :: ItemKind+cruiseAdHologram,       outerAdHologram, victoriaClassHologram, allureIntroHologram, spreadNitrogen, spreadNitrogen8, focusedNitrogen, paintSpray, currentDischarge, currentRecharge :: ItemKind  -- We take care (e.g., in burningOil below) that blasts are not faster -- than 100% fastest natural speed, or some frames would be skipped,@@ -36,50 +153,81 @@  -- * Parameterized blasts -burningOil :: Int -> ItemKind-burningOil n = ItemKind+spreadBurningOil :: Int -> GroupName ItemKind -> ItemKind+spreadBurningOil n grp = ItemKind   { isymbol  = '*'   , iname    = "burning oil"-  , ifreq    = [(toGroupName $ "burning oil" <+> tshow n, 1)]+  , ifreq    = [(grp, 1), (FIRE_SOURCE, 1), (OIL_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]-  , icount   = intToDice (4 + n * 4)+  , icount   = intToDice (4 + n * 2)   , irarity  = [(1, 1)]   , iverbHit = "sear"   , iweight  = 1   , idamage  = 0-  , iaspects = [ toVelocity (min 100 $ n `div` 2 * 10)+  , iaspects = [ toVelocity (max 10 $ min 100 $ n `div` 2 * 10)                , SetFlag Fragile, SetFlag Blast                , AddSkill SkShine 2 ]   , ieffects = [ Burn 1-               , toOrganBad "pacified" (1 `d` 2) ]+               , toOrganBad S_PACIFIED (2 + 1 `d` 2) ]                    -- slips and frantically puts out fire   , idesc    = "Sticky oil, burning brightly."   , ikit     = []   }-burningOil2 = burningOil 2  -- 2 steps, 2 turns-burningOil3 = burningOil 3  -- 3 steps, 2 turns-burningOil4 = burningOil 4  -- 4 steps, 2 turns+spreadBurningOil2 = spreadBurningOil 2 S_VIOLENT_BURNING_OIL_2+                      -- 2 steps, 2 turns+spreadBurningOil3 = spreadBurningOil 3 S_VIOLENT_BURNING_OIL_3+                      -- 2 steps, 2 turns+spreadBurningOil4 = spreadBurningOil 4 S_VIOLENT_BURNING_OIL_4+                      -- 4 steps, 2 turns+spreadBurningOil8 :: Int -> GroupName ItemKind -> ItemKind+spreadBurningOil8 n grp = (spreadBurningOil (n `div` 2) grp)+  { icount   = 7  -- 8 was too deadly+  }+spreadBurningOil82 = spreadBurningOil8 2 S_BURNING_OIL_2+spreadBurningOil83 = spreadBurningOil8 3 S_BURNING_OIL_3+spreadBurningOil84 = spreadBurningOil8 4 S_BURNING_OIL_4+focusedBurningOil :: Int -> GroupName ItemKind -> GroupName ItemKind -> ItemKind+focusedBurningOil n grp grpExplode = ItemKind+  { isymbol  = '`'+  , iname    = "igniting oil"+  , ifreq    = [(grp, 1), (FIRE_SOURCE, 1), (OIL_SOURCE, 1)]+  , iflavour = zipPlain [BrYellow]  -- all ignitions yellow to avoid appearing+                                    -- as a small vial explosion+  , icount   = intToDice n+  , irarity  = [(1, 1)]+  , iverbHit = "ignite"+  , iweight  = 1+  , idamage  = 0+  , iaspects = [ toLinger 0  -- 0 steps, 1 turn+               , SetFlag Fragile, SetFlag Blast ]+      -- when the target position is occupied, the explosion starts one step+      -- away, hence we set range to 0 steps, to limit dispersal+  , ieffects = [OnSmash $ Explode grpExplode]+  , idesc    = idesc spreadBurningOil2+  , ikit     = []+  }+focusedBurningOil2 = focusedBurningOil 2 S_FOCUSED_BURNING_OIL_2 S_BURNING_OIL_2+focusedBurningOil3 = focusedBurningOil 3 S_FOCUSED_BURNING_OIL_3 S_BURNING_OIL_3+focusedBurningOil4 = focusedBurningOil 4 S_FOCUSED_BURNING_OIL_4 S_BURNING_OIL_4 firecracker :: Int -> ItemKind firecracker n = ItemKind   { isymbol  = '*'   , iname    = "firecracker"-  , ifreq    = [(toGroupName $ if n == 5-                               then "firecracker"-                               else "firecracker" <+> tshow n, 1)]+  , ifreq    = [(if n == 5+                 then S_FIRECRACKER+                 else firecrackerAt n, 1)]   , iflavour = zipPlain [brightCol !! ((n + 2) `mod` length brightCol)]   , icount   = if n <= 3 then 1 `d` min 2 n else 2 + 1 `d` 2   , irarity  = [(1, 1)]   , iverbHit = if n >= 4 then "singe" else "crack"   , iweight  = 1   , idamage  = 0-  , iaspects = [ toVelocity 5+  , iaspects = [ toVelocity 5  -- 1 step, 1 turn                , SetFlag Fragile, SetFlag Blast                , AddSkill SkShine $ intToDice $ 1 + n `div` 2 ]   , ieffects = [if n >= 4 then Burn 1 else RefillCalm (-2)]-               ++ [DropBestWeapon | n >= 4]-               ++ [ OnSmash $ Explode-                    $ toGroupName $ "firecracker" <+> tshow (n - 1)-                  | n >= 2 ]+               ++ [Discharge 1 30 | n >= 3]+               ++ [OnSmash $ Explode $ firecrackerAt (n - 1) | n >= 2]   , idesc    = "Scraps of burnt paper, covering little pockets of black powder, buffeted by colorful explosions."   , ikit     = []   }@@ -94,9 +242,9 @@ spreadFragmentation = ItemKind   { isymbol  = '*'   , iname    = "fragmentation burst"-  , ifreq    = [("violent fragmentation", 1)]+  , ifreq    = [(S_VIOLENT_FRAGMENTATION, 1), (FIRE_SOURCE, 1)]   , iflavour = zipPlain [Red]-  , icount   = 16  -- strong but few, so not always hits target+  , icount   = 8  -- strong but few, so not always hits target   , irarity  = [(1, 1)]   , iverbHit = "tear apart"   , iweight  = 1@@ -105,14 +253,13 @@   , iaspects = [ ToThrow $ ThrowMod 100 20 4  -- 4 steps, 1 turn                , SetFlag Lobable, SetFlag Fragile, SetFlag Blast                , AddSkill SkShine 3, AddSkill SkHurtMelee $ -12 * 5 ]-  , ieffects = [DropItem 1 1 COrgan "condition"]+  , ieffects = [DropItem 1 1 COrgan CONDITION]   , idesc    = "Flying shards, flame and smoke."   , ikit     = []   } spreadFragmentation8 = spreadFragmentation-  { iname    = "fragmentation burst"-  , ifreq    = [("fragmentation", 1)]-  , icount   = 8+  { ifreq    = [(S_FRAGMENTATION, 1), (FIRE_SOURCE, 1)]+  , icount   = 5   , iaspects = [ ToThrow $ ThrowMod 100 10 2  -- 2 steps, 1 turn                , SetFlag Lobable, SetFlag Fragile, SetFlag Blast                , AddSkill SkShine 3, AddSkill SkHurtMelee $ -12 * 5 ]@@ -121,9 +268,9 @@ focusedFragmentation = ItemKind   { isymbol  = '`'   , iname    = "deflagration ignition"  -- improvised fertilizer, etc.-  , ifreq    = [("focused fragmentation", 1)]+  , ifreq    = [(S_FOCUSED_FRAGMENTATION, 1), (FIRE_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]-  , icount   = 4  -- 32 in total vs 16; on average 4 hits+  , icount   = 3  -- 15 in total vs 8, higher spread   , irarity  = [(1, 1)]   , iverbHit = "ignite"   , iweight  = 1@@ -132,16 +279,18 @@                , SetFlag Fragile, SetFlag Blast ]       -- when the target position is occupied, the explosion starts one step       -- away, hence we set range to 0 steps, to limit dispersal-  , ieffects = [OnSmash $ Explode "fragmentation"]+  , ieffects = [OnSmash $ Explode S_FRAGMENTATION]   , idesc    = idesc spreadFragmentation   , ikit     = []   } spreadConcussion = ItemKind   { isymbol  = '*'   , iname    = "concussion blast"-  , ifreq    = [("violent concussion", 1)]+  , ifreq    = [(S_VIOLENT_CONCUSSION, 1), (BLAST_SOURCE, 1)]+                 -- only the strongest explosion breaches   , iflavour = zipPlain [Magenta]-  , icount   = 16+  , icount   = 12  -- pushing sometimes gets the victim out of attacker range,+                   -- but also sometimes moves to a position hit again later   , irarity  = [(1, 1)]   , iverbHit = "shock"   , iweight  = 1@@ -153,18 +302,18 @@       -- outdoors it has short range, but we only model indoors in the game;       -- it's much faster than black powder shock wave, but we are beyond       -- human-noticeable speed differences on short distances anyway-  , ieffects = [ DropItem maxBound 1 CEqp "misc armor"+  , ieffects = [ DropItem maxBound 1 CEqp ARMOR_MISC                , PushActor (ThrowMod 400 25 1)  -- 1 step, fast; after DropItem                    -- this produces spam for braced actors; too bad-               , toOrganBad "immobile" 3  -- no balance-               , toOrganBad "deafened" 23 ]-  , idesc    = "Shock wave, hot gases, some fire and smoke."+               , toOrganBad S_IMMOBILE 3  -- no balance+               , toOrganBad S_DEAFENED 23 ]+  , idesc    = "Shock wave, hot gases and smoke. Able to demolish small obstacles."   , ikit     = []   } spreadConcussion8 = spreadConcussion-  { iname    = "concussion blast"-  , ifreq    = [("concussion", 1)]-  , icount   = 8+  { ifreq    = [(S_CONCUSSION, 1), (BLAST_SOURCE, 1)]+                 -- only the strongest explosion breaches+  , icount   = 6   , iaspects = [ ToThrow $ ThrowMod 100 10 2  -- 2 steps, 1 turn                , SetFlag Lobable, SetFlag Fragile, SetFlag Blast                , AddSkill SkShine 3, AddSkill SkHurtMelee $ -8 * 5 ]@@ -172,25 +321,25 @@ focusedConcussion = ItemKind   { isymbol  = '`'   , iname    = "detonation ignition"  -- stabilized high explosive liquid-  , ifreq    = [("focused concussion", 1)]+  , ifreq    = [(S_FOCUSED_CONCUSSION, 1), (BLAST_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]-  , icount   = 4+  , icount   = 4  -- 24 in total vs 12, higher spread, less harm from pushing   , irarity  = [(1, 1)]   , iverbHit = "ignite"   , iweight  = 1   , idamage  = 0   , iaspects = [ toLinger 0  -- 0 steps, 1 turn                , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [OnSmash $ Explode "concussion"]+  , ieffects = [OnSmash $ Explode S_CONCUSSION]   , idesc    = idesc spreadConcussion   , ikit     = []   } spreadFlash = ItemKind   { isymbol  = '`'   , iname    = "magnesium flash"  -- or aluminum, but let's stick to one-  , ifreq    = [("violent flash", 1)]+  , ifreq    = [(S_VIOLENT_FLASH, 1), (FIRE_SOURCE, 1)]   , iflavour = zipPlain [BrWhite]-  , icount   = 16+  , icount   = 13   , irarity  = [(1, 1)]   , iverbHit = "dazzle"   , iweight  = 1@@ -198,17 +347,20 @@   , iaspects = [ ToThrow $ ThrowMod 100 20 4  -- 4 steps, 1 turn                , SetFlag Fragile, SetFlag Blast                , AddSkill SkShine 5 ]-  , ieffects = [toOrganBad "blind" 5, toOrganBad "weakened" 20]+  , ieffects = [ toOrganBad S_WEAKENED 20+               , Discharge 1 $ 40 - 1 `d` 20 ]                  -- Wikipedia says: blind for five seconds and afterimage-                 -- for much longer, harming aim-  , idesc    = "A very bright flash of fire."+                 -- for much longer, harming aim. But blind is boring+                 -- and prevents the player from seeing fun explosions+                 -- such as this one.+  , idesc    = "A very bright flash of fire, causing long-lasting afterimages."   , ikit     = []   } spreadFlash8 = spreadFlash   { iname    = "spark"-  , ifreq    = [("spark", 1)]-  , icount   = 8-  , iverbHit = "blind"+  , ifreq    = [(S_SPARK, 1), (FIRE_SOURCE, 1)]+  , icount   = 5+  , iverbHit = "singe"   , iaspects = [ ToThrow $ ThrowMod 100 10 2  -- 2 steps, 1 turn                , SetFlag Fragile, SetFlag Blast                , AddSkill SkShine 5 ]@@ -216,22 +368,22 @@ focusedFlash = ItemKind   { isymbol  = '`'   , iname    = "magnesium ignition"-  , ifreq    = [("focused flash", 1)]+  , ifreq    = [(S_FOCUSED_FLASH, 1), (FIRE_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]-  , icount   = 4+  , icount   = 5  -- 25 in total vs 13, higher spread   , irarity  = [(1, 1)]   , iverbHit = "ignite"   , iweight  = 1   , idamage  = 0   , iaspects = [ toLinger 0  -- 0 steps, 1 turn                , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [OnSmash $ Explode "spark"]+  , ieffects = [OnSmash $ Explode S_SPARK]   , idesc    = idesc spreadFlash   , ikit     = []   } singleSpark = spreadFlash   { iname    = "single spark"-  , ifreq    = [("single spark", 1)]+  , ifreq    = [(S_SINGLE_SPARK, 1)]  -- too weak to start a fire   , icount   = 1   , iverbHit = "spark"   , iaspects = [ toLinger 5  -- 1 step, 1 turn@@ -244,14 +396,14 @@ glassPiece = ItemKind   { isymbol  = '*'   , iname    = "glass piece"-  , ifreq    = [("glass hail", 1)]+  , ifreq    = [(S_GLASS_HAIL, 1)]   , iflavour = zipPlain [Blue]-  , icount   = 8+  , icount   = 6   , irarity  = [(1, 1)]   , iverbHit = "cut"   , iweight  = 1   , idamage  = 2 `d` 1-  , iaspects = [ ToThrow $ ThrowMod 100 20 4  -- 4 steps, 1 turn+  , iaspects = [ ToThrow $ ThrowMod 100 10 4  -- 2 steps, 1 turn                , SetFlag Fragile, SetFlag Blast                , AddSkill SkHurtMelee $ -15 * 5 ]                  -- brittle, not too dense; armor blocks@@ -260,29 +412,29 @@   , ikit     = []   } focusedGlass = glassPiece  -- when blowing up windows-  { ifreq    = [("focused glass hail", 1)]-  , icount   = 4+  { ifreq    = [(S_FOCUSED_GLASS_HAIL, 1)]+  , icount   = 2   , iaspects = [ toLinger 0  -- 0 steps, 1 turn                , SetFlag Fragile, SetFlag Blast                , AddSkill SkHurtMelee $ -15 * 5 ]-  , ieffects = [OnSmash $ Explode "glass hail"]+  , ieffects = [OnSmash $ Explode S_GLASS_HAIL]   } --- * Assorted blasts don't induce conditions or not mainly so+-- * Assorted blasts that don't induce conditions or not used mainly for them  fragrance = ItemKind   { isymbol  = '`'   , iname    = "fragrance"  -- instant, fast fragrance-  , ifreq    = [("fragrance", 1)]+  , ifreq    = [(S_FRAGRANCE, 1)]   , iflavour = zipPlain [Magenta]   , icount   = 12   , irarity  = [(1, 1)]   , iverbHit = "engulf"   , iweight  = 1   , idamage  = 0-  , iaspects = [ toLinger 10  -- 2 steps, 1 turn+  , iaspects = [ ToThrow $ ThrowMod 200 5 1  -- 2 steps, .5 turn (necklaces)                , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [Impress, toOrganGood "rose-smelling" 45]+  , ieffects = [Impress, toOrganGood S_ROSE_SMELLING 45]   -- Linger 10, because sometimes it takes 2 turns due to starting just   -- before actor turn's end (e.g., via a necklace).   , idesc    = "A pleasant scent."@@ -291,7 +443,7 @@ pheromone = ItemKind   { isymbol  = '`'   , iname    = "musky whiff"  -- a kind of mist rather than fragrance-  , ifreq    = [("pheromone", 1)]+  , ifreq    = [(S_PHEROMONE, 1)]   , iflavour = zipPlain [BrMagenta]   , icount   = 16   , irarity  = [(1, 1)]@@ -307,7 +459,7 @@ mistCalming = ItemKind  -- unused   { isymbol  = '`'   , iname    = "mist"-  , ifreq    = [("calming mist", 1)]+  , ifreq    = [(S_CALMING_MIST, 1)]   , iflavour = zipPlain [BrGreen]   , icount   = 8   , irarity  = [(1, 1)]@@ -323,7 +475,7 @@ odorDistressing = ItemKind   { isymbol  = '`'   , iname    = "distressing whiff"-  , ifreq    = [("distressing odor", 1)]+  , ifreq    = [(S_DISTRESSING_ODOR, 1)]   , iflavour = zipFancy [BrRed]  -- salmon   , icount   = 8   , irarity  = [(1, 1)]@@ -333,15 +485,15 @@   , iaspects = [ toLinger 10  -- 2 steps, 1 turn                , SetFlag Fragile, SetFlag Blast ]   , ieffects = [ RefillCalm (-10)-               , toOrganBad "foul-smelling" (20 + 1 `d` 5)-               , toOrganBad "impatient" (2 + 1 `d` 2) ]+               , toOrganBad S_FOUL_SMELLING (20 + 1 `d` 5)+               , toOrganBad S_IMPATIENT (2 + 1 `d` 2) ]   , idesc    = "It turns the stomach."  -- and so can't stand still   , ikit     = []   } mistHealing = ItemKind   { isymbol  = '`'   , iname    = "mist"  -- powerful, so slow and narrow-  , ifreq    = [("healing mist", 1)]+  , ifreq    = [(S_HEALING_MIST, 1)]   , iflavour = zipFancy [BrGreen]   , icount   = 8   , irarity  = [(1, 1)]@@ -358,7 +510,7 @@ mistHealing2 = ItemKind   { isymbol  = '`'   , iname    = "mist"-  , ifreq    = [("healing mist 2", 1)]+  , ifreq    = [(S_HEALING_MIST_2, 1)]   , iflavour = zipPlain [Green]   , icount   = 8   , irarity  = [(1, 1)]@@ -375,7 +527,7 @@ mistWounding = ItemKind   { isymbol  = '`'   , iname    = "mist"-  , ifreq    = [("wounding mist", 1)]+  , ifreq    = [(S_WOUNDING_MIST, 1)]   , iflavour = zipPlain [BrRed]   , icount   = 8   , irarity  = [(1, 1)]@@ -388,10 +540,10 @@   , idesc    = "The air itself stings and itches."   , ikit     = []   }-distortion = ItemKind+distortion = ItemKind  -- currently unused   { isymbol  = 'v'   , iname    = "vortex"-  , ifreq    = [("distortion", 1)]+  , ifreq    = [(S_DISTORTION, 1)]   , iflavour = zipPlain [White]   , icount   = 8  -- braced are immune to Teleport; avoid failure messages   , irarity  = [(1, 1)]@@ -404,27 +556,28 @@   , idesc    = "The air shifts oddly, as though light is being warped."   , ikit     = []   }-smoke = ItemKind  -- when stuff burns out  -- unused+smoke = ItemKind  -- slow, long, weakly blinding blast; smoke, octopus's ink   { isymbol  = '`'-  , iname    = "smoke fume"  -- pluralizes better than 'smokes'-  , ifreq    = [("smoke", 1)]+  , iname    = "dark fume"  -- pluralizes better than 'smokes'+  , ifreq    = [(S_SMOKE, 1)]   , iflavour = zipPlain [BrBlack]   , icount   = 16   , irarity  = [(1, 1)]-  , iverbHit = "choke"  -- or "obscure"+  , iverbHit = "obscure"   , iweight  = 1   , idamage  = 0   , iaspects = [ toVelocity 20  -- 4 steps, 2 turns                , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [toOrganBad "withholding" (5 + 1 `d` 3)]-                  -- choking and tears, can roughly see, but not aim-  , idesc    = "Twirling clouds of grey smoke."+  , ieffects = [ toOrganBad S_BLIND (1 + 1 `d` 2)+               , toOrganBad S_WITHHOLDING (5 + 1 `d` 3) ]+                  -- choking and tears or just obscured view, can't aim+  , idesc    = "Twirling clouds of black, sticky vapour."   , ikit     = []   } boilingWater = ItemKind   { isymbol  = '*'   , iname    = "boiling water"-  , ifreq    = [("boiling water", 1)]+  , ifreq    = [(S_BOILING_WATER, 1)]  -- not enough water to create puddles   , iflavour = zipPlain [White]   , icount   = 18   , irarity  = [(1, 1)]@@ -440,14 +593,14 @@ glue = ItemKind   { isymbol  = '*'   , iname    = "glue droplet"-  , ifreq    = [("glue", 1)]+  , ifreq    = [(S_GLUE, 1)]   , iflavour = zipPlain [Cyan]   , icount   = 8  -- Paralyze doesn't stack; avoid failure messages   , irarity  = [(1, 1)]   , iverbHit = "glue"   , iweight  = 1   , idamage  = 0-  , iaspects = [ toVelocity 20  -- 4 steps, 2 turns+  , iaspects = [ toVelocity 10  -- 2 steps, 2 turns                , SetFlag Fragile, SetFlag Blast ]   , ieffects = [Paralyze 10]   , idesc    = "Thick and clinging."@@ -456,7 +609,7 @@ waste = ItemKind   { isymbol  = '*'   , iname    = "waste piece"-  , ifreq    = [("waste", 1)]+  , ifreq    = [(S_WASTE, 1)]   , iflavour = zipPlain [Brown]   , icount   = 16   , irarity  = [(1, 1)]@@ -464,15 +617,15 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [ toOrganBad "foul-smelling" (30 + 1 `d` 10)-               , toOrganBad "dispossessed" (10 + 1 `d` 5) ]+  , ieffects = [ toOrganBad S_FOUL_SMELLING (30 + 1 `d` 10)+               , toOrganBad S_DISPOSSESSED (10 + 1 `d` 5) ]   , idesc    = "Sodden and foul-smelling."   , ikit     = []   } mistAntiSlow = ItemKind   { isymbol  = '`'   , iname    = "mist"-  , ifreq    = [("anti-slow mist", 1)]+  , ifreq    = [(S_ANTI_SLOW_MIST, 1)]   , iflavour = zipFancy [BrYellow]   , icount   = 8   , irarity  = [(1, 1)]@@ -481,14 +634,14 @@   , idamage  = 0   , iaspects = [ toVelocity 5  -- 1 step, 1 turn                , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [DropItem 1 1 COrgan "slowed"]+  , ieffects = [DropItem 1 1 COrgan S_SLOWED]   , idesc    = "A cleansing rain."   , ikit     = []   } mistAntidote = ItemKind   { isymbol  = '`'   , iname    = "mist"-  , ifreq    = [("antidote mist", 1)]+  , ifreq    = [(S_ANTIDOTE_MIST, 1)]   , iflavour = zipFancy [BrBlue]   , icount   = 8   , irarity  = [(1, 1)]@@ -497,14 +650,14 @@   , idamage  = 0   , iaspects = [ toVelocity 5  -- 1 step, 1 turn                , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [DropItem 1 maxBound COrgan "poisoned"]+  , ieffects = [DropItem 1 maxBound COrgan S_POISONED]   , idesc    = "Washes away death's dew."   , ikit     = []   } mistSleep = ItemKind   { isymbol  = '`'   , iname    = "mist"-  , ifreq    = [("sleep mist", 1)]+  , ifreq    = [(S_SLEEP_MIST, 1)]   , iflavour = zipFancy [BrMagenta]   , icount   = 8   , irarity  = [(1, 1)]@@ -528,7 +681,7 @@ denseShower = ItemKind   { isymbol  = '`'   , iname    = "dense shower"-  , ifreq    = [("dense shower", 1)]+  , ifreq    = [(S_DENSE_SHOWER, 1)]   , iflavour = zipFancy [Green]   , icount   = 12   , irarity  = [(1, 1)]@@ -536,14 +689,14 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganGood "strengthened" 5]+  , ieffects = [toOrganGood S_STRENGTHENED 5]   , idesc    = "A thick rain of droplets."   , ikit     = []   } sparseShower = ItemKind   { isymbol  = '`'   , iname    = "sparse shower"-  , ifreq    = [("sparse shower", 1)]+  , ifreq    = [(S_SPARSE_SHOWER, 1)]   , iflavour = zipFancy [Red]   , icount   = 8   , irarity  = [(1, 1)]@@ -551,29 +704,32 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganBad "weakened" 7]+  , ieffects = [toOrganBad S_WEAKENED 7]   , idesc    = "Light droplets that cling to clothing."   , ikit     = []   } protectingBalmMelee = ItemKind   { isymbol  = '`'-  , iname    = "balm droplet"-  , ifreq    = [("melee protective balm", 1)]+  , iname    = "oil spray"+  , ifreq    = [(S_MELEE_PROTECTIVE_BALM, 1), (OIL_SOURCE, 1)]   , iflavour = zipFancy [Brown]-  , icount   = 16+  , icount   = 6   , irarity  = [(1, 1)]-  , iverbHit = "balm"+  , iverbHit = "old"   , iweight  = 1   , idamage  = 0-  , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganGood "protected from melee" (3 + 1 `d` 3)]-  , idesc    = "A thick ointment that hardens the skin."+  , iaspects = [ toLinger 0  -- 0 steps, 1 turn+                   -- this from a faucent won't ever hit anyone, which is fine,+                   -- but from a flask it would+               , SetFlag Fragile, SetFlag Blast ]+  , ieffects = [toOrganGood S_PROTECTED_FROM_MELEE (3 + 1 `d` 3)]+  , idesc    = "Thin and slippery."   , ikit     = []   } protectingBalmRanged = ItemKind   { isymbol  = '`'   , iname    = "balm droplet"-  , ifreq    = [("ranged protective balm", 1)]+  , ifreq    = [(S_RANGE_PROTECTIVE_BALM, 1)]   , iflavour = zipPlain [BrYellow]   , icount   = 16   , irarity  = [(1, 1)]@@ -581,29 +737,29 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganGood "protected from ranged" (3 + 1 `d` 3)]-  , idesc    = "Grease that protects from flying death."+  , ieffects = [toOrganGood S_PROTECTED_FROM_RANGED (3 + 1 `d` 3)]+  , idesc    = "A thick ointment that hardens under high speed stress."   , ikit     = []   }-vulnerabilityBalm = ItemKind+defenselessnessRunout = ItemKind   { isymbol  = '`'-  , iname    = "fluorescent paint"-  , ifreq    = [("fluorescent paint", 1)]-  , iflavour = zipPlain [BrRed]+  , iname    = "acid spray"+  , ifreq    = [(S_DEFENSELESSNESS_RUNOUT, 1)]+  , iflavour = zipFancy [BrRed]   , icount   = 16   , irarity  = [(1, 1)]-  , iverbHit = "paint"+  , iverbHit = "dissolve"   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganBad "painted" (3 + 1 `d` 3)]-  , idesc    = "Softly glowing red pain that marks a target."+  , ieffects = [toOrganBad S_DEFENSELESS (3 + 1 `d` 3)]+  , idesc    = "A searing fluid that sticks to the skin."   , ikit     = []   } resolutionDust = ItemKind   { isymbol  = '`'   , iname    = "resolution dust"-  , ifreq    = [("resolution dust", 1)]+  , ifreq    = [(S_RESOLUTION_DUST, 1)]   , iflavour = zipPlain [Brown]   , icount   = 16   , irarity  = [(1, 1)]@@ -611,7 +767,7 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganGood "resolute" (3 + 1 `d` 3)]+  , ieffects = [toOrganGood S_RESOLUTE (3 + 1 `d` 3)]                  -- short enough duration that @calmEnough@ not a big problem   , idesc    = "A handful of honest earth, to strengthen the soul."   , ikit     = []@@ -619,7 +775,7 @@ hasteSpray = ItemKind   { isymbol  = '`'   , iname    = "haste spray"-  , ifreq    = [("haste spray", 1)]+  , ifreq    = [(S_HASTE_SPRAY, 1)]   , iflavour = zipFancy [BrYellow]   , icount   = 16   , irarity  = [(1, 1)]@@ -627,30 +783,14 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganGood "hasted" (3 + 1 `d` 3)]+  , ieffects = [toOrganGood S_HASTED (3 + 1 `d` 3)]   , idesc    = "A quick spurt."   , ikit     = []   }-slownessMist = ItemKind-  { isymbol  = '`'-  , iname    = "slowness mist"-  , ifreq    = [("slowness mist", 1)]-  , iflavour = zipPlain [BrBlue]-  , icount   = 8-  , irarity  = [(1, 1)]-  , iverbHit = "slow"-  , iweight  = 0-  , idamage  = 0-  , iaspects = [toVelocity 5, SetFlag Fragile, SetFlag Blast]-                 -- 1 step, 1 turn, mist, slow-  , ieffects = [toOrganBad "slowed" (3 + 1 `d` 3)]-  , idesc    = "Clammy fog, making each movement an effort."-  , ikit     = []-  } eyeDrop = ItemKind   { isymbol  = '`'   , iname    = "eye drop"-  , ifreq    = [("eye drop", 1)]+  , ifreq    = [(S_EYE_DROP, 1)]   , iflavour = zipFancy [BrCyan]   , icount   = 16   , irarity  = [(1, 1)]@@ -658,14 +798,14 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganGood "far-sighted" (3 + 1 `d` 3)]+  , ieffects = [toOrganGood S_FAR_SIGHTED (3 + 1 `d` 3)]   , idesc    = "Not to be taken orally."   , ikit     = []   }-ironFiling = ItemKind+ironFiling = ItemKind  -- fast, short, strongly blinding blast   { isymbol  = '`'   , iname    = "iron filing"-  , ifreq    = [("iron filing", 1)]+  , ifreq    = [(S_IRON_FILING, 1)]   , iflavour = zipPlain [Red]   , icount   = 16   , irarity  = [(1, 1)]@@ -673,14 +813,14 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganBad "blind" (10 + 1 `d` 10)]+  , ieffects = [toOrganBad S_BLIND (10 + 1 `d` 10)]   , idesc    = "A shaving of bright metal."   , ikit     = []   } smellyDroplet = ItemKind   { isymbol  = '`'   , iname    = "smelly droplet"-  , ifreq    = [("smelly droplet", 1)]+  , ifreq    = [(S_SMELLY_DROPLET, 1)]   , iflavour = zipFancy [Blue]   , icount   = 16   , irarity  = [(1, 1)]@@ -688,14 +828,14 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganGood "keen-smelling" (5 + 1 `d` 3)]+  , ieffects = [toOrganGood S_KEEN_SMELLING (5 + 1 `d` 3)]   , idesc    = "A viscous lump that stains the skin."   , ikit     = []   } eyeShine = ItemKind   { isymbol  = '`'   , iname    = "eye shine"-  , ifreq    = [("eye shine", 1)]+  , ifreq    = [(S_EYE_SHINE, 1)]   , iflavour = zipFancy [Cyan]   , icount   = 16   , irarity  = [(1, 1)]@@ -703,14 +843,14 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganGood "shiny-eyed" (3 + 1 `d` 3)]+  , ieffects = [toOrganGood S_SHINY_EYED (3 + 1 `d` 3)]   , idesc    = "They almost glow in the dark."   , ikit     = []   } whiskeySpray = ItemKind   { isymbol  = '`'   , iname    = "whiskey spray"-  , ifreq    = [("whiskey spray", 1)]+  , ifreq    = [(S_WHISKEY_SPRAY, 1)]   , iflavour = zipFancy [Brown]   , icount   = 16   , irarity  = [(1, 1)]@@ -718,14 +858,14 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [toOrganGood "drunk" (3 + 1 `d` 3)]+  , ieffects = [toOrganGood S_DRUNK (3 + 1 `d` 3)]   , idesc    = "It burns in the best way."   , ikit     = []   } youthSprinkle = ItemKind   { isymbol  = '`'   , iname    = "youth sprinkle"-  , ifreq    = [("youth sprinkle", 1)]+  , ifreq    = [(S_YOUTH_SPRINKLE, 1)]   , iflavour = zipFancy [BrGreen]   , icount   = 16   , irarity  = [(1, 1)]@@ -733,32 +873,49 @@   , iweight  = 1   , idamage  = 0   , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]-  , ieffects = [ toOrganGood "rose-smelling" (40 + 1 `d` 20)-               , toOrganNoTimer "regenerating" ]+  , ieffects = [ toOrganGood S_ROSE_SMELLING (40 + 1 `d` 20)+               , toOrganNoTimer S_REGENERATING ]   , idesc    = "Bright and smelling of the Spring."   , ikit     = []   } poisonCloud = ItemKind   { isymbol  = '`'   , iname    = "poison cloud"-  , ifreq    = [("poison cloud", 1)]+  , ifreq    = [(S_POISON_CLOUD, 1)]   , iflavour = zipFancy [BrMagenta]-  , icount   = 16+  , icount   = 11  -- low, to be less deadly in a tunnel, compared to single hit   , irarity  = [(1, 1)]   , iverbHit = "poison"-  , iweight  = 0+  , iweight  = 0  -- lingers, blocking path   , idamage  = 0   , iaspects = [ ToThrow $ ThrowMod 10 100 2  -- 2 steps, 2 turns                , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [toOrganNoTimer "poisoned"]+  , ieffects = [toOrganNoTimer S_POISONED]   , idesc    = "Choking gas that stings the eyes."   , ikit     = []   }-blastNoStat :: Text -> ItemKind+pingFlash = ItemKind+  { isymbol  = '`'+  , iname    = "flash"+  , ifreq    = [(S_PING_PLASH, 1)]+  , iflavour = zipFancy [Green]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "ping"+  , iweight  = 1  -- to prevent blocking the way+  , idamage  = 0+  , iaspects = [ ToThrow $ ThrowMod 200 0 1  -- 1 step, .5 turn (necklaces)+               , SetFlag Fragile, SetFlag Blast+               , AddSkill SkShine 2 ]+  , ieffects = [OnSmash Yell]+  , idesc    = "A ping and a display flash from an echolocator out of sync momentarily."+  , ikit     = []+  }+blastNoStat :: GroupName ItemKind -> ItemKind blastNoStat grp = ItemKind   { isymbol  = '`'   , iname    = "mist"-  , ifreq    = [(toGroupName $ grp <+> "mist", 1)]+  , ifreq    = [(blastNoStatOf grp, 1)]   , iflavour = zipFancy [White]   , icount   = 12   , irarity  = [(1, 1)]@@ -767,23 +924,23 @@   , idamage  = 0   , iaspects = [ toVelocity 10  -- 2 steps, 2 turns                , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [toOrganBad (toGroupName grp) (3 + 1 `d` 3)]+  , ieffects = [toOrganBad grp (3 + 1 `d` 3)]   , idesc    = "Completely disables one personal faculty."   , ikit     = []   }-blastNoSkMove = blastNoStat "immobile"-blastNoSkMelee = blastNoStat "pacified"-blastNoSkDisplace = blastNoStat "irreplaceable"-blastNoSkAlter = blastNoStat "retaining"-blastNoSkWait = blastNoStat "impatient"-blastNoSkMoveItem = blastNoStat "dispossessed"-blastNoSkProject = blastNoStat "withholding"-blastNoSkApply = blastNoStat "parsimonious"-blastBonusStat :: Text -> ItemKind+blastNoSkMove = blastNoStat S_IMMOBILE+blastNoSkMelee = blastNoStat S_PACIFIED+blastNoSkDisplace = blastNoStat S_IRREPLACEABLE+blastNoSkAlter = blastNoStat S_RETAINING+blastNoSkWait = blastNoStat S_IMPATIENT+blastNoSkMoveItem = blastNoStat S_DISPOSSESSED+blastNoSkProject = blastNoStat S_WITHHOLDING+blastNoSkApply = blastNoStat S_PARSIMONIOUS+blastBonusStat :: GroupName ItemKind -> ItemKind blastBonusStat grp = ItemKind   { isymbol  = '`'   , iname    = "dew"-  , ifreq    = [(toGroupName $ grp <+> "dew", 1)]+  , ifreq    = [(blastBonusStatOf grp, 1)]   , iflavour = zipFancy [White]   , icount   = 12   , irarity  = [(1, 1)]@@ -792,18 +949,18 @@   , idamage  = 0   , iaspects = [ toVelocity 10  -- 2 steps, 2 turns                , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [toOrganGood (toGroupName grp) (20 + 1 `d` 5)]+  , ieffects = [toOrganGood grp (20 + 1 `d` 5)]   , idesc    = "Temporarily enhances the given personal faculty."   , ikit     = []   }-blastBonusSkMove = blastBonusStat "more mobile"-blastBonusSkMelee = blastBonusStat "more combative"-blastBonusSkDisplace = blastBonusStat "more displacing"-blastBonusSkAlter = blastBonusStat "more altering"-blastBonusSkWait = blastBonusStat "more patient"-blastBonusSkMoveItem = blastBonusStat "more tidy"-blastBonusSkProject = blastBonusStat "more projecting"-blastBonusSkApply = blastBonusStat "more practical"+blastBonusSkMove = blastBonusStat S_MORE_MOBILE+blastBonusSkMelee = blastBonusStat S_MORE_COMBATIVE+blastBonusSkDisplace = blastBonusStat S_MORE_DISPLACING+blastBonusSkAlter = blastBonusStat S_MORE_MODIFYING+blastBonusSkWait = blastBonusStat S_MORE_PATIENT+blastBonusSkMoveItem = blastBonusStat S_MORE_TIDY+blastBonusSkProject = blastBonusStat S_MORE_PROJECTING+blastBonusSkApply = blastBonusStat S_MORE_PRACTICAL  -- * Allure-specific @@ -821,7 +978,7 @@ cruiseAdHologram = ItemKind   { isymbol  = '`'   , iname    = "cruise ad hologram"-  , ifreq    = [("cruise ad hologram", 1), ("advertisement", 10)]+  , ifreq    = [(S_RHINO_HOLOGRAM, 1), (ADVERTISEMENT, 10)]   , iflavour = zipFancy [BrMagenta]   , icount   = 8   , irarity  = [(1, 1)]@@ -830,20 +987,20 @@   , idamage  = 0   , iaspects = [ toVelocity 5  -- 1 step, 1 turn                , SetFlag Fragile, SetFlag Blast ]-  , ieffects = [toOrganGood "resolute" (5 + 1 `d` 2), DropBestWeapon]-  , idesc    = "The fitful holographic clip shows a couple that laughs, watches in silence Saturn's rings through a huge window, throws treats to a little rhino frolicking in reduced gravity, runs through corridors wearing alien masks in a mock chase. An exited female voice proclaims: \"...safety, security and comfort...for each of your senses...personalized life support zones...robot servants...guessing your every wish...\""+  , ieffects = [toOrganGood S_RESOLUTE (5 + 1 `d` 2), Discharge 1 50]+  , idesc    = "The fitful holographic clip shows a couple that laughs, watches in silence Saturn's rings through a huge window, throws treats to Billy the little rhino frolicking in reduced gravity, runs through corridors wearing alien masks in a mock chase. An exited female voice proclaims: \"...safety, security and comfort...for each of your senses...personalized life support zones...robot servants...guessing your every wish...\""   , ikit     = []   } outerAdHologram = cruiseAdHologram   { iname    = "cruise ad hologram"-  , ifreq    = [("advertisement", 20)]+  , ifreq    = [(ADVERTISEMENT, 20)]   , icount   = 4   , ieffects = []  -- weak, 4 particles, no effect   , idesc    = "A composed young man in a hat looks straight into your eyes with unwavering stare and extols the opportunities, freedom and excitement of the outer Solar System frontier life with unshakable conviction. Names of Neptune-area realtors scroll at the bottom in small font with oversize serifs."   } victoriaClassHologram = outerAdHologram   { iname    = "space fleet hologram"-  , ifreq    = [("story-telling", 20)]+  , ifreq    = [(STORY_TELLING, 20)]   , iflavour = zipFancy [BrBlue]   , icount   = 1   , iverbHit = "bore"@@ -851,6 +1008,88 @@   } allureIntroHologram = victoriaClassHologram   { iname    = "spaceship hologram"-  , ifreq    = [("story-telling", 10)]+  , ifreq    = [(STORY_TELLING, 10)]   , idesc    = "A wavy 3D wireframe of a spaceship rotates ponderously. Male voice drones: Allure of the Stars belongs to a long line of luxurious orbit-to-orbit cruise liners, the Victoria-class. The ship is named after the largest passenger sea vessel of the early 21st century, with which it shares the grandeur and extravagance. This particular Victoria-class specimen has been designed for long cruises to gas giants, their moons and the moon cities (with their notorious saloons and night life). It has a meteor shield in the form of a flat, multi-layer. unpressurized cargo bay covering the front plane. Such extra cargo capacity enables long space journeys with no limits on resource usage. On shorter legs of the journeys it also enables opportunistic mass cargo transport (in accordance to strictest regulations and completely isolated from the airflow on passenger decks), which is always in demand at the profusely productive, but scarcely populated Solar System frontier. It also makes the unit much thicker than usual: the length from the tip of the cargo bay to the engines' exhausts is almost two thirds of the diameter of the disk. All in all, it is a particularly sturdy and self-sufficient member of a class famed for exceptional resilience and safety."+  }++-- ** Misc++spreadNitrogen = ItemKind+  { isymbol  = '`'+  , iname    = "nitrogen mist"+  , ifreq    = [(S_VIOLENT_SLOWNESS_MIST, 1), (COLD_SOURCE, 1)]+  , iflavour = zipPlain [BrBlack]+  , icount   = 15+  , irarity  = [(1, 1)]+  , iverbHit = "freeze"+  , iweight  = 1+  , idamage  = 0+  , iaspects = [ toVelocity 10  -- 2 steps, 2 turns, mist, slow+               , SetFlag Fragile, SetFlag Blast ]+  , ieffects = [toOrganBad S_SLOWED (2 + 1 `d` 3)]+  , idesc    = "Colourless cold clammy fog, making each movement an effort."+  , ikit     = []+  }+spreadNitrogen8 = spreadNitrogen+  { ifreq    = [(S_SLOWNESS_MIST, 1), (COLD_SOURCE, 1)]+  , icount   = 7+  , iaspects = [ toVelocity 5  -- 1 step, 1 turn, mist, slow+               , SetFlag Fragile, SetFlag Blast ]+  }+focusedNitrogen = ItemKind+  { isymbol  = '`'+  , iname    = "liquid nitrogen droplet"+  , ifreq    = [(S_FOCUSED_SLOWNESS_MIST, 1), (COLD_SOURCE, 1)]+  , iflavour = zipFancy [White]+  , icount   = 4 -- 28 in total vs 15, higher spread+  , irarity  = [(1, 1)]+  , iverbHit = "freeze"+  , iweight  = 1+  , idamage  = 0+  , iaspects = [ toLinger 0  -- 0 steps, 1 turn+               , SetFlag Fragile, SetFlag Blast ]+  , ieffects = [ OnSmash $ Explode S_SLOWNESS_MIST+               , toOrganBad S_SLOWED (2 + 1 `d` 3) ]+  , idesc    = "Colourless and colder than ice."+  , ikit     = []+  }+paintSpray = ItemKind+  { isymbol  = '`'+  , iname    = "fluorescent paint"+  , ifreq    = [(S_PAINT_DROPLET, 1)]+  , iflavour = zipPlain [BrRed]+  , icount   = 16+  , irarity  = [(1, 1)]+  , iverbHit = "paint"+  , iweight  = 1+  , idamage  = 0+  , iaspects = [toLinger 10, SetFlag Fragile, SetFlag Blast]+  , ieffects = [toOrganBad S_PAINTED (3 + 1 `d` 3)]+  , idesc    = "Softly glowing red paint that marks a target."+  , ikit     = []+  }+currentDischarge = ItemKind+  { isymbol  = 'v'+  , iname    = "static current"+  , ifreq    = [(S_CURRENT_DISCHARGE, 1)]+  , iflavour = zipFancy [BrBlue]+  , icount   = 12+  , irarity  = [(1, 1)]+  , iverbHit = "reset"+  , iweight  = 1+  , idamage  = 0+  , iaspects = [ ToThrow $ ThrowMod 200 5 1  -- 2 steps, .5 turn (necklaces)+               , SetFlag Fragile, SetFlag Blast ]+  , ieffects = [Discharge 2 $ 80 - 1 `d` 40]+                 -- does this prevent hunger too easily, when exploited?+  , idesc    = ""+  , ikit     = []+  }+currentRecharge = currentDischarge+  { ifreq    = [(S_CURRENT_RECHARGE, 1)]+  , iflavour = zipFancy [BrRed]+  , ieffects = [Recharge 5 999]+                 -- beneficial, except for harmful periodic effects;+                 -- does this speed healing via Spur Life too much?+  , idesc    = ""   }
GameDefinition/Content/ItemKindEmbed.hs view
@@ -1,34 +1,189 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Definitions of items embedded in map tiles. module Content.ItemKindEmbed-  ( embeds+  ( -- * Group name patterns+    pattern SCRATCH_ON_WALL, pattern OBSCENE_PICTOGRAM, pattern SUBTLE_FRESCO, pattern SIGNAGE, pattern SMALL_FIRE, pattern SMALL_FIRE_5, pattern BIG_FIRE, pattern FROST, pattern RUBBLE, pattern DOORWAY_TRAP_UNKNOWN, pattern DOORWAY_TRAP, pattern STAIRS_UP, pattern STAIRS_DOWN, pattern ESCAPE, pattern STAIRS_TRAP_UP, pattern STAIRS_TRAP_DOWN, pattern LECTERN, pattern SHALLOW_WATER, pattern STRAIGHT_PATH, pattern FROZEN_GROUND+  , pattern S_SANDSTONE_ROCK+  , pattern STAIRS_UP_OUTDOOR, pattern STAIRS_DOWN_OUTDOOR, pattern ABANDONED_CACHE, pattern JEWELRY_DISPLAY_TRAP, pattern BLACK_STARRY_SKY, pattern DISENGAGED_DOCKING_GEAR, pattern RUINED_FIRST_AID_KIT, pattern FIRE_FIGHTING_GEAR, pattern DISPLAY_3D, pattern CRACKED_FLUE, pattern BLOOD_ON_WALL, pattern DEPOSIT_BOX, pattern JEWELRY_CASE, pattern EDIBLE_PLANT_RIPE, pattern STAIRS_TRAP_DOWN_OIL, pattern DOOR_TRAP_PUSH, pattern LIFT_UP, pattern LIFT_DOWN, pattern LIFT_TRAP, pattern SHUTTLE_HARDWARE, pattern OIL_PUDDLE, pattern DECONTAMINATION_CHAMBER, pattern BARREL_CONTENTS, pattern WORKSHOP_BENCH+  , pattern MUSEAL, pattern EDIBLE_PLANT, pattern FIRE_FIGHTING_ITEM, pattern STEEL_SCRAP, pattern HANDLE, pattern HANDLE_AND_STEEL, pattern POLE_AND_STEEL, pattern SPACESUIT_PART, pattern THICK_CLOTH, pattern PERFUME, pattern STARTING_HAMMER, pattern CLOTH_RAG+  , pattern S_ENCHANCED_BERRY, pattern S_COOKED_BERRY, pattern S_FRAYED_FUNGUS, pattern S_COOKED_FUNGUS, pattern S_THIC_LEAF, pattern S_COOKED_LEAF, pattern S_RECONFIGURED_FRUIT, pattern S_COOKED_FRUIT, pattern S_FRAGRANT_HERB, pattern S_COOKED_HERB, pattern S_DULL_FLOWER, pattern S_COOKED_FLOWER, pattern S_SPICY_BARK, pattern S_COOKED_BARK, pattern S_PUMPKIN, pattern S_COOKED_PUMPKIN, pattern S_REFRIGERATION_COIL, pattern S_DOUSED_WOODEN_TORCH, pattern S_DOUSED_OIL_LAMP, pattern S_OIL_LAMP, pattern S_ROSE_WATER_FLASK, pattern S_WATER_FLASK, pattern S_SPACESUIT_JACKET, pattern S_SPACESUIT_TROUSERS, pattern S_SPACESUIT_GLOVE, pattern S_SPACESUIT_HELMET, pattern S_SPACESUIT_BOOT, pattern S_SPACESUIT, pattern S_SPACESUIT_TORN, pattern S_HARPOON_CARGO, pattern S_HARPOON_SHARP, pattern S_SHIELD_BLUNT, pattern S_SHIELD_SHARP, pattern S_SHORT_BLUNT_HAMMER, pattern S_LONG_BLUNT_HAMMER, pattern S_SHORT_SHARP_HAMMER, pattern S_LONG_SHARP_HAMMER, pattern S_CLEAVER, pattern S_DAGGER, pattern S_RAPIER_BLUNT, pattern S_RAPIER_SHARP, pattern S_POLE_CLEAVER, pattern S_LONG_SPEAR, pattern S_SHORT_CLUB, pattern S_LONG_CLUB, pattern S_CROWBAR, pattern S_FIRE_AXE, pattern S_POLL_AXE, pattern S_HALBERD_BLUNT, pattern S_HALBERD_SHARP, pattern S_STAFF, pattern S_PIPE, pattern S_SHARPENED_PIPE+  , embedsGNSingleton, embedsGN+  , -- * Content+    embeds   ) where  import Prelude ()  import Game.LambdaHack.Core.Prelude +import Content.ItemKindActor+import Content.ItemKindBlast+import Content.ItemKindOrgan+import Content.ItemKindTemporary import Game.LambdaHack.Content.ItemKind+import Game.LambdaHack.Content.TileKind (floorSymbol) import Game.LambdaHack.Core.Dice import Game.LambdaHack.Definition.Ability import Game.LambdaHack.Definition.Color import Game.LambdaHack.Definition.Defs import Game.LambdaHack.Definition.Flavour +-- * Group name patterns++embedsGNSingleton :: [GroupName ItemKind]+embedsGNSingleton =+     S_SANDSTONE_ROCK+     : [S_ENCHANCED_BERRY, S_COOKED_BERRY, S_FRAYED_FUNGUS, S_COOKED_FUNGUS, S_THIC_LEAF, S_COOKED_LEAF, S_RECONFIGURED_FRUIT, S_COOKED_FRUIT, S_FRAGRANT_HERB, S_COOKED_HERB, S_DULL_FLOWER, S_COOKED_FLOWER, S_SPICY_BARK, S_COOKED_BARK, S_PUMPKIN, S_COOKED_PUMPKIN, S_REFRIGERATION_COIL, S_DOUSED_WOODEN_TORCH, S_DOUSED_OIL_LAMP, S_OIL_LAMP, S_ROSE_WATER_FLASK, S_WATER_FLASK, S_SPACESUIT_JACKET, S_SPACESUIT_TROUSERS, S_SPACESUIT_GLOVE, S_SPACESUIT_HELMET, S_SPACESUIT_BOOT, S_SPACESUIT, S_SPACESUIT_TORN, S_HARPOON_CARGO, S_HARPOON_SHARP, S_SHIELD_BLUNT, S_SHIELD_SHARP, S_SHORT_BLUNT_HAMMER, S_LONG_BLUNT_HAMMER, S_SHORT_SHARP_HAMMER, S_LONG_SHARP_HAMMER, S_CLEAVER, S_DAGGER, S_RAPIER_BLUNT, S_RAPIER_SHARP, S_POLE_CLEAVER, S_LONG_SPEAR, S_SHORT_CLUB, S_LONG_CLUB, S_CROWBAR, S_FIRE_AXE, S_POLL_AXE, S_HALBERD_BLUNT, S_HALBERD_SHARP, S_STAFF, S_PIPE, S_SHARPENED_PIPE]++pattern S_SANDSTONE_ROCK :: GroupName ItemKind++pattern S_ENCHANCED_BERRY, S_COOKED_BERRY, S_FRAYED_FUNGUS, S_COOKED_FUNGUS, S_THIC_LEAF, S_COOKED_LEAF, S_RECONFIGURED_FRUIT, S_COOKED_FRUIT, S_FRAGRANT_HERB, S_COOKED_HERB, S_DULL_FLOWER, S_COOKED_FLOWER, S_SPICY_BARK, S_COOKED_BARK, S_PUMPKIN, S_COOKED_PUMPKIN, S_REFRIGERATION_COIL, S_DOUSED_WOODEN_TORCH, S_DOUSED_OIL_LAMP, S_OIL_LAMP, S_ROSE_WATER_FLASK, S_WATER_FLASK, S_SPACESUIT_JACKET, S_SPACESUIT_TROUSERS, S_SPACESUIT_GLOVE, S_SPACESUIT_HELMET, S_SPACESUIT_BOOT, S_SPACESUIT, S_SPACESUIT_TORN, S_HARPOON_CARGO, S_HARPOON_SHARP, S_SHIELD_BLUNT, S_SHIELD_SHARP, S_SHORT_BLUNT_HAMMER, S_LONG_BLUNT_HAMMER, S_SHORT_SHARP_HAMMER, S_LONG_SHARP_HAMMER, S_CLEAVER, S_DAGGER, S_RAPIER_BLUNT, S_RAPIER_SHARP, S_POLE_CLEAVER, S_LONG_SPEAR, S_SHORT_CLUB, S_LONG_CLUB, S_CROWBAR, S_FIRE_AXE, S_POLL_AXE, S_HALBERD_BLUNT, S_HALBERD_SHARP, S_STAFF, S_PIPE, S_SHARPENED_PIPE :: GroupName ItemKind++embedsGN :: [GroupName ItemKind]+embedsGN =+       [SCRATCH_ON_WALL, OBSCENE_PICTOGRAM, SUBTLE_FRESCO, SIGNAGE, SMALL_FIRE, SMALL_FIRE_5, BIG_FIRE, FROST, RUBBLE, DOORWAY_TRAP_UNKNOWN, DOORWAY_TRAP, STAIRS_UP, STAIRS_DOWN, ESCAPE, STAIRS_TRAP_UP, STAIRS_TRAP_DOWN, LECTERN, SHALLOW_WATER, STRAIGHT_PATH, FROZEN_GROUND]+    ++ [STAIRS_UP_OUTDOOR, STAIRS_DOWN_OUTDOOR, ABANDONED_CACHE, JEWELRY_DISPLAY_TRAP, BLACK_STARRY_SKY, DISENGAGED_DOCKING_GEAR, RUINED_FIRST_AID_KIT, FIRE_FIGHTING_GEAR, DISPLAY_3D, CRACKED_FLUE, BLOOD_ON_WALL, DEPOSIT_BOX, JEWELRY_CASE, EDIBLE_PLANT_RIPE, STAIRS_TRAP_DOWN_OIL, DOOR_TRAP_PUSH, LIFT_UP, LIFT_DOWN, LIFT_TRAP, SHUTTLE_HARDWARE, OIL_PUDDLE, DECONTAMINATION_CHAMBER, BARREL_CONTENTS, WORKSHOP_BENCH]+    ++ [MUSEAL, EDIBLE_PLANT, FIRE_FIGHTING_ITEM, STEEL_SCRAP, HANDLE, HANDLE_AND_STEEL, POLE_AND_STEEL, SPACESUIT_PART, THICK_CLOTH, PERFUME, STARTING_HAMMER, CLOTH_RAG]++pattern SCRATCH_ON_WALL, OBSCENE_PICTOGRAM, SUBTLE_FRESCO, SIGNAGE, SMALL_FIRE, SMALL_FIRE_5, BIG_FIRE, FROST, RUBBLE, DOORWAY_TRAP_UNKNOWN, DOORWAY_TRAP, STAIRS_UP, STAIRS_DOWN, ESCAPE, STAIRS_TRAP_UP, STAIRS_TRAP_DOWN, LECTERN, SHALLOW_WATER, STRAIGHT_PATH, FROZEN_GROUND :: GroupName ItemKind++pattern STAIRS_UP_OUTDOOR, STAIRS_DOWN_OUTDOOR, ABANDONED_CACHE, JEWELRY_DISPLAY_TRAP, BLACK_STARRY_SKY, DISENGAGED_DOCKING_GEAR, RUINED_FIRST_AID_KIT, FIRE_FIGHTING_GEAR, DISPLAY_3D, CRACKED_FLUE, BLOOD_ON_WALL, DEPOSIT_BOX, JEWELRY_CASE, EDIBLE_PLANT_RIPE, STAIRS_TRAP_DOWN_OIL, DOOR_TRAP_PUSH, LIFT_UP, LIFT_DOWN, LIFT_TRAP, SHUTTLE_HARDWARE, OIL_PUDDLE, DECONTAMINATION_CHAMBER, BARREL_CONTENTS, WORKSHOP_BENCH :: GroupName ItemKind++pattern MUSEAL, EDIBLE_PLANT, FIRE_FIGHTING_ITEM, STEEL_SCRAP, HANDLE, HANDLE_AND_STEEL, POLE_AND_STEEL, SPACESUIT_PART, THICK_CLOTH, PERFUME, STARTING_HAMMER, CLOTH_RAG :: GroupName ItemKind++pattern SCRATCH_ON_WALL = GroupName "scratch on wall"+pattern OBSCENE_PICTOGRAM = GroupName "obscene pictogram"+pattern SUBTLE_FRESCO = GroupName "subtle fresco"+pattern SIGNAGE = GroupName "signage"+pattern SMALL_FIRE = GroupName "small fire"+pattern SMALL_FIRE_5 = GroupName "small fire embers"+pattern BIG_FIRE = GroupName "big fire"+pattern FROST = GroupName "frozen mass"+pattern RUBBLE = GroupName "rubble"+pattern DOORWAY_TRAP_UNKNOWN = GroupName "doorway trap unknown"+pattern DOORWAY_TRAP = GroupName "doorway trap"+pattern STAIRS_UP = GroupName "stairs up"+pattern STAIRS_DOWN = GroupName "stairs down"+pattern ESCAPE = GroupName "escape"+pattern STAIRS_TRAP_UP = GroupName "stairs trap up"+pattern STAIRS_TRAP_DOWN = GroupName "stairs trap down"+pattern LECTERN = GroupName "lectern"+pattern SHALLOW_WATER = GroupName "shallow water"+pattern STRAIGHT_PATH = GroupName "straight path"+pattern FROZEN_GROUND = GroupName "frozen ground"++pattern S_SANDSTONE_ROCK = GroupName "sandstone rock"++-- ** Allure-specific+pattern STAIRS_UP_OUTDOOR = GroupName "stairs outdoor up"+pattern STAIRS_DOWN_OUTDOOR = GroupName "stairs outdoor down"+pattern ABANDONED_CACHE = GroupName "abandoned cache"+pattern JEWELRY_DISPLAY_TRAP = GroupName "jewelry display trap"+pattern BLACK_STARRY_SKY = GroupName "black starry sky"+pattern DISENGAGED_DOCKING_GEAR = GroupName "disengaged docking gear"+pattern RUINED_FIRST_AID_KIT = GroupName "ruined first aid kit"+pattern FIRE_FIGHTING_GEAR = GroupName "fire fighting gear"+pattern DISPLAY_3D = GroupName "3D display"+pattern CRACKED_FLUE = GroupName "cracked flue"+pattern BLOOD_ON_WALL = GroupName "blood on wall"+pattern DEPOSIT_BOX = GroupName "deposit box"+pattern JEWELRY_CASE = GroupName "reinforced glass case"+pattern EDIBLE_PLANT_RIPE = GroupName "edible vegetation"+pattern STAIRS_TRAP_DOWN_OIL = GroupName "oil staircase trap"+pattern DOOR_TRAP_PUSH = GroupName "weak door frame"+pattern LIFT_UP = GroupName "lift up"+pattern LIFT_DOWN = GroupName "lift down"+pattern LIFT_TRAP = GroupName "lift trap"+pattern SHUTTLE_HARDWARE = GroupName "shuttle hardware"+pattern OIL_PUDDLE = GroupName "oil puddle"+pattern DECONTAMINATION_CHAMBER = GroupName "decontamination chamber"+pattern BARREL_CONTENTS = GroupName "barrel contents"+pattern WORKSHOP_BENCH = GroupName "workshop bench"++pattern MUSEAL = GroupName "museal item"+pattern EDIBLE_PLANT = GroupName "edible plant"+pattern FIRE_FIGHTING_ITEM = GroupName "fire fighting item"+pattern STEEL_SCRAP = GroupName "steel scrap"+pattern HANDLE = GroupName "handle"+pattern HANDLE_AND_STEEL = GroupName "steel on a handle"+pattern POLE_AND_STEEL = GroupName "steel on a pole"+pattern SPACESUIT_PART = GroupName "spacesuit part"+pattern THICK_CLOTH = GroupName "thick cloth"+pattern PERFUME = GroupName "perfume"+pattern STARTING_HAMMER = GroupName "starting hammer"+pattern CLOTH_RAG = GroupName "cloth rag"++pattern S_ENCHANCED_BERRY = GroupName "enhanced berry"+pattern S_COOKED_BERRY = GroupName "cooked berry"+pattern S_FRAYED_FUNGUS = GroupName "frayed fungus"+pattern S_COOKED_FUNGUS = GroupName "cooked fungus"+pattern S_THIC_LEAF = GroupName "thick leaf"+pattern S_COOKED_LEAF = GroupName "cooked leaf"+pattern S_RECONFIGURED_FRUIT = GroupName "reconfigured fruit"+pattern S_COOKED_FRUIT = GroupName "cooked fruit"+pattern S_FRAGRANT_HERB = GroupName "fragrant herb"+pattern S_COOKED_HERB = GroupName "cooked herb"+pattern S_DULL_FLOWER = GroupName "dull flower"+pattern S_COOKED_FLOWER = GroupName "cooked flower"+pattern S_SPICY_BARK = GroupName "spicy bark"+pattern S_COOKED_BARK = GroupName "cooked bark"+pattern S_PUMPKIN = GroupName "pumpkin"+pattern S_COOKED_PUMPKIN = GroupName "cooked pumpkin"++pattern S_DOUSED_WOODEN_TORCH = GroupName "doused wooden torch"+pattern S_DOUSED_OIL_LAMP = GroupName "doused oil lamp"+pattern S_OIL_LAMP = GroupName "oil lamp"+pattern S_REFRIGERATION_COIL = GroupName "refrigeration coil"+pattern S_ROSE_WATER_FLASK = GroupName "rose water flask"+pattern S_WATER_FLASK = GroupName "water flask"+pattern S_SPACESUIT_JACKET = GroupName "spacesuit jacket"+pattern S_SPACESUIT_TROUSERS = GroupName "pair of space trousers"+pattern S_SPACESUIT_GLOVE = GroupName "spacesuit glove"+pattern S_SPACESUIT_HELMET = GroupName "spacesuit helmet"+pattern S_SPACESUIT_BOOT = GroupName "spacesuit boot"+pattern S_SPACESUIT = GroupName "spacesuit"+pattern S_SPACESUIT_TORN = GroupName "torn spacesuit"+pattern S_HARPOON_CARGO = GroupName "blunt harpoon"+pattern S_HARPOON_SHARP = GroupName "sharp harpoon"+pattern S_SHIELD_BLUNT = GroupName "blunted shield"+pattern S_SHIELD_SHARP = GroupName "spiked shield"+pattern S_SHORT_BLUNT_HAMMER = GroupName "sledgehammer"+pattern S_LONG_BLUNT_HAMMER = GroupName "maul"+pattern S_SHORT_SHARP_HAMMER = GroupName "sharp sledgehammer"+pattern S_LONG_SHARP_HAMMER = GroupName "sharp maul"+pattern S_CLEAVER = GroupName "cleaver"+pattern S_DAGGER = GroupName "dagger"+pattern S_RAPIER_BLUNT = GroupName "blunt rapier"+pattern S_RAPIER_SHARP = GroupName "sharp rapier"+pattern S_POLE_CLEAVER = GroupName "pole cleaver"+pattern S_LONG_SPEAR = GroupName "long spear"+pattern S_SHORT_CLUB = GroupName "short club"+pattern S_LONG_CLUB = GroupName "long club"+pattern S_CROWBAR = GroupName "crowbar"+pattern S_FIRE_AXE = GroupName "fire axe"+pattern S_POLL_AXE = GroupName "poll axe"+pattern S_HALBERD_BLUNT = GroupName "blunt halberd"+pattern S_HALBERD_SHARP = GroupName "sharp halberd"+pattern S_STAFF = GroupName "staff"+pattern S_PIPE = GroupName "pipe"+pattern S_SHARPENED_PIPE = GroupName "sharpened pipe"++-- * Content+ embeds :: [ItemKind] embeds =-  [scratchOnWall, obscenePictogram, subtleFresco, treasureCache, treasureCacheTrap, signboardExit, signboardEmbed, signboardMerchandise, fireSmall, fireBig, frost, rubble, doorwayTrapTemplate, doorwayTrap1, doorwayTrap2, doorwayTrap3, stairsUp, stairsDown, escape, staircaseTrapUp, staircaseTrapDown, pulpit, shallowWater, straightPath, frozenGround]+  [scratchOnWall, obscenePictogram, subtleFresco, treasureCache, treasureCacheTrap, signageExit, signageEmbed, signageMerchandise, fireSmall, fireSmall5, fireBig, frost, rubble, doorwayTrapTemplate, doorwayTrap1, doorwayTrap2, doorwayTrap3, stairsUp, stairsDown, escape, stairsTrapUp, stairsTrapDown, lectern, shallowWater, straightPath, frozenGround]   -- Allure-specific-  ++ [blackStarrySky, disengagedDocking, ruinedFirstAidKit, wall3dBillboard, depositBox, jewelryCase, liftUp, liftDown, liftTrap, liftTrap2, shuttleHardware, machineOil, crudeWeld, decontaminator]+  ++ [stairsUpOutdoor, stairsDownOutdoor, blackStarrySky, disengagedDocking, desertedAirlock, ruinedFirstAidKit, fireFightingGear, fireFightingGearIntact, wall3dBillboard, crackedFlue, bloodOnWall, bloodOnWall2, bloodOnWall3, depositBox, depositBoxSummonHero, depositBoxSummonMonster, jewelryCase, ediblePlantRipe, stairsTrapDownOil, doorTrapPush, liftUp, liftDown, liftTrap, liftTrap2, liftTrap3, shuttleHardware, machineOil, crudeWeld, decontaminator, barrelFuel, barrelFertilizer, barrelOxidizer, barrelOil, barrelNitrogen, workshopBench, signageExitLuggage, signageEmbedLuggage, signageMerchandiseLuggage] -scratchOnWall,    obscenePictogram, subtleFresco, treasureCache, treasureCacheTrap, signboardExit, signboardEmbed, signboardMerchandise, fireSmall, fireBig, frost, rubble, doorwayTrapTemplate, doorwayTrap1, doorwayTrap2, doorwayTrap3, stairsUp, stairsDown, escape, staircaseTrapUp, staircaseTrapDown, pulpit, shallowWater, straightPath, frozenGround :: ItemKind+scratchOnWall,    obscenePictogram, subtleFresco, treasureCache, treasureCacheTrap, signageExit, signageEmbed, signageMerchandise, fireSmall, fireSmall5, fireBig, frost, rubble, doorwayTrapTemplate, doorwayTrap1, doorwayTrap2, doorwayTrap3, stairsUp, stairsDown, escape, stairsTrapUp, stairsTrapDown, lectern, shallowWater, straightPath, frozenGround :: ItemKind -- Allure-specific-blackStarrySky,       disengagedDocking, ruinedFirstAidKit, wall3dBillboard, depositBox, jewelryCase, liftUp, liftDown, liftTrap, liftTrap2, shuttleHardware, machineOil, crudeWeld, decontaminator :: ItemKind+stairsUpOutdoor,       stairsDownOutdoor, blackStarrySky, disengagedDocking, desertedAirlock, ruinedFirstAidKit, fireFightingGear, fireFightingGearIntact, wall3dBillboard, crackedFlue, bloodOnWall, bloodOnWall2, bloodOnWall3, depositBox, depositBoxSummonHero, depositBoxSummonMonster, jewelryCase, ediblePlantRipe, stairsTrapDownOil, doorTrapPush, liftUp, liftDown, liftTrap, liftTrap2, liftTrap3, shuttleHardware, machineOil, crudeWeld, decontaminator, barrelFuel, barrelFertilizer, barrelOxidizer, barrelOil, barrelNitrogen, workshopBench, signageExitLuggage, signageEmbedLuggage, signageMerchandiseLuggage :: ItemKind  -- Make sure very few walls are substantially useful, e.g., caches, -- and none that are secret. Otherwise the player will spend a lot of time@@ -39,7 +194,7 @@ scratchOnWall = ItemKind   { isymbol  = '?'   , iname    = "claw mark"-  , ifreq    = [("scratch on wall", 1)]+  , ifreq    = [(SCRATCH_ON_WALL, 1)]   , iflavour = zipPlain [BrBlack]   , icount   = 1   , irarity  = [(1, 1)]@@ -47,7 +202,7 @@   , iweight  = 1000   , idamage  = 0   , iaspects = [SetFlag Durable]-  , ieffects = [ VerbMsg "start making sense of the scratches"+  , ieffects = [ VerbMsg "start making sense of the scratches" "."                , Detect DetectHidden 3 ]   , idesc    = "A seemingly random series of scratches, carved deep into the wall."   , ikit     = []@@ -55,7 +210,7 @@ obscenePictogram = ItemKind   { isymbol  = '*'   , iname    = "repulsing graffiti"-  , ifreq    = [("obscene pictogram", 1)]+  , ifreq    = [(OBSCENE_PICTOGRAM, 1)]   , iflavour = zipPlain [BrMagenta]   , icount   = 1   , irarity  = [(1, 1)]@@ -63,35 +218,34 @@   , iweight  = 1000   , idamage  = 0   , iaspects = [Timeout 7, SetFlag Durable]-  , ieffects = [ VerbMsg "enter inexplicable rage at a glimpse of the inscrutable graffiti"+  , ieffects = [ VerbMsg "enter inexplicable rage at a glimpse of the inscrutable graffiti!" ""                , RefillCalm (-20)-               , OneOf [ toOrganGood "strengthened" (3 + 1 `d` 2)-                       , CreateItem CInv "sandstone rock" timerNone ] ]+               , OneOf [ toOrganGood S_STRENGTHENED (3 + 1 `d` 2)+                       , CreateItem Nothing CGround S_SANDSTONE_ROCK timerNone ]+               ]   , idesc    = ""  -- alien writing? or runaway robot AI?   , ikit     = []   } subtleFresco = ItemKind   { isymbol  = '*'   , iname    = "subtle mural"-  , ifreq    = [("subtle fresco", 1)]+  , ifreq    = [(SUBTLE_FRESCO, 1)]   , iflavour = zipPlain [BrGreen]   , icount   = 1   , irarity  = [(1, 1)]   , iverbHit = "sooth"   , iweight  = 1000   , idamage  = 0-  , iaspects = [Timeout 7, SetFlag Durable]-  , ieffects = [ VerbMsg "feel refreshed by the subtle fresco"-               , toOrganGood "far-sighted" (3 + 1 `d` 2)-               , toOrganGood "keen-smelling" (3 + 1 `d` 2) ]-                 -- hearing gets a boost through bracing, so no need here+  , iaspects = [Timeout 10, SetFlag Durable]+  , ieffects = [ VerbMsg "feel refreshed by the subtle fresco" "."+               , toOrganGood S_FAR_SIGHTED (5 + 1 `d` 2) ]   , idesc    = "Expensive yet tasteful."   , ikit     = []   } treasureCache = ItemKind-  { isymbol  = '0'+  { isymbol  = 'o'   , iname    = "set"-  , ifreq    = [("abandoned cache", 1)]+  , ifreq    = [(ABANDONED_CACHE, 1)]   , iflavour = zipPlain [BrBlue]   , icount   = 1   , irarity  = [(1, 1)]@@ -99,14 +253,16 @@   , iweight  = 10000   , idamage  = 0   , iaspects = [ELabel "of odds and ends", SetFlag Durable]-  , ieffects = [CreateItem CGround "common item" timerNone]+  , ieffects = [CreateItem Nothing CGround COMMON_ITEM timerNone]   , idesc    = "If this stash is hidden, it's in plain sight. Or, more probably, it's just tucked aside so that it doesn't get in the way. Whomever worked there, apparently failed to return and retrieve his belongings."   , ikit     = []   }+reliefMsg :: Effect+reliefMsg = VerbMsg "sigh with relief when nothing explodes in your face!" "" treasureCacheTrap = ItemKind   { isymbol  = '^'   , iname    = "anti-theft protection"-  , ifreq    = [("jewelry display trap", 1)]+  , ifreq    = [(JEWELRY_DISPLAY_TRAP, 1)]   , iflavour = zipPlain [Red]   , icount   = 1   , irarity  = [(1, 1)]@@ -114,20 +270,20 @@   , iweight  = 1000   , idamage  = 0   , iaspects = []  -- not Durable, springs at most once-  , ieffects = [OneOf [ toOrganBad "blind" (10 + 1 `d` 10)+  , ieffects = [OneOf [ toOrganBad S_BLIND (10 + 1 `d` 10)                       , RefillCalm (-99)-                      , Explode "focused concussion"-                      , RefillCalm (-1), RefillCalm (-1), RefillCalm (-1) ]]+                      , Explode S_FOCUSED_CONCUSSION+                      , reliefMsg, reliefMsg ]]   , idesc    = "A display of such kingly trinkets warrants an autonomous guarding device. The precaution is particularly understandable if some of the merchandise is capable of instantly frying video monitoring equipment across the hall."   , ikit     = []   }-signboardExit = ItemKind-  { isymbol  = '?'+signageExit = ItemKind+  { isymbol  = '0'   , iname    = "sticker"-  , ifreq    = [("signboard", 50)]-  , iflavour = zipPlain [BrMagenta]+  , ifreq    = [(SIGNAGE, 100)]+  , iflavour = zipPlain [BrGreen]   , icount   = 1-  , irarity  = [(1, 1)]+  , irarity  = [(1, 0), (2, 1)]   , iverbHit = "whack"   , iweight  = 10000   , idamage  = 0@@ -137,48 +293,60 @@                  -- This is a rare tile so use it to convey some more backstory.   , ikit     = []   }-signboardEmbed = signboardExit+signageEmbed = signageExit   { iname    = "notice"-  , ifreq    = [("signboard", 50)]+  , ifreq    = [(SIGNAGE, 100)]+  , iflavour = zipPlain [Cyan]   , ieffects = [Detect DetectEmbed 12]  -- low tech, hence fully operational   , idesc    = "Detailed schematics for the maintenance crew."                  -- This is a rare tile so use it to convey some more backstory.   }-signboardMerchandise = signboardExit+signageMerchandise = signageExit   { iname    = "shop list"-  , ifreq    = [("signboard", 50)]+  , ifreq    = [(SIGNAGE, 100)]+  , iflavour = zipPlain [BrCyan]   , ieffects = [Detect DetectLoot 20]  -- high tech, so slightly confused-  , idesc    = "A list of nearby commercial outlets, constantly updated by tracking merchandise not registered as passenger property. Customers are kindly asked to refrain from littering in this heavily monitored public area."+  , idesc    = "A list of nearby commercial outlets, constantly updated by tracking merchandise not registered as passenger property. Customers are kindly requeted to refrain from littering in this heavily monitored public area."   } fireSmall = ItemKind-  { isymbol  = '%'-  , iname    = "small fire"-  , ifreq    = [("small fire", 1)]+  { isymbol  = 'o'+  , iname    = "tiny fire"+  , ifreq    = [(SMALL_FIRE, 1), (FIRE_SOURCE, 1)]   , iflavour = zipPlain [BrRed]   , icount   = 1   , irarity  = [(1, 1)]   , iverbHit = "burn"   , iweight  = 10000   , idamage  = 0-  , iaspects = [SetFlag Durable]-  , ieffects = [Burn 1, Explode "single spark"]-  , idesc    = "A few small logs, burning brightly."+  , iaspects = [ELabel "of roasting", SetFlag Durable]+  , ieffects = [ Burn 1, Explode S_SINGLE_SPARK+               , OnCombine roastEffect ]+  , idesc    = "A few embers and wisps of flame, glowing brightly."   , ikit     = []   }+fireSmall5 = fireSmall+  { iname    = "small fire"  -- whenever a lot of mass to burn, e.g., bush, oil+  , ifreq    = [(SMALL_FIRE_5, 1), (FIRE_SOURCE, 1)]+  , iflavour = zipPlain [Red]+  , ieffects = [ Burn 1, Explode S_SINGLE_SPARK+               , OnCombine roastEffect5 ]+  } fireBig = fireSmall   { isymbol  = '0'   , iname    = "big fire"-  , ifreq    = [("big fire", 1)]+  , ifreq    = [(BIG_FIRE, 1), (FIRE_SOURCE, 1)]+  , iflavour = zipPlain [Red]+  , iaspects = [ELabel "of immolation", SetFlag Durable]   , ieffects = [ Burn 2-               , CreateItem CInv "wooden torch" timerNone-               , Explode "spark" ]+               , CreateItem Nothing CGround S_WOODEN_TORCH timerNone+               , Explode S_SPARK ]   , idesc    = "Glowing with light and warmth."   , ikit     = []   } frost = ItemKind   { isymbol  = '^'-  , iname    = "frost"-  , ifreq    = [("frost", 1)]+  , iname    = "frozen mass"+  , ifreq    = [(FROST, 1), (COLD_SOURCE, 1)]   , iflavour = zipPlain [BrBlue]   , icount   = 1   , irarity  = [(1, 1)]@@ -189,13 +357,13 @@   , ieffects = [ Burn 1  -- sensory ambiguity between hot and cold                , RefillCalm 20  -- cold reason                , PushActor (ThrowMod 400 10 1) ]  -- slippery ice-  , idesc    = "Intricate patterns of shining ice."+  , idesc    = "Intricate patterns of shining ice. Too voluminous to be thawed, but fragile enough to be shattered."   , ikit     = []   } rubble = ItemKind   { isymbol  = '&'   , iname    = "rubble"-  , ifreq    = [("rubble", 1)]+  , ifreq    = [(RUBBLE, 1)]   , iflavour = zipPlain [BrYellow]   , icount   = 1   , irarity  = [(1, 1)]@@ -203,50 +371,54 @@   , iweight  = 100000   , idamage  = 0   , iaspects = [SetFlag Durable]-  , ieffects = [OneOf [ Explode "focused glass hail"-                      , Summon "mobile animal" $ 1 `dL` 2-                      , toOrganNoTimer "poisoned"-                      , CreateItem CGround "common item" timerNone-                      , RefillCalm (-1), RefillCalm (-1), RefillCalm (-1)-                      , RefillCalm (-1), RefillCalm (-1), RefillCalm (-1) ]]+  , ieffects = [OneOf [ Explode S_FOCUSED_GLASS_HAIL+                      , Summon MOBILE_ANIMAL $ 1 `dL` 2+                      , toOrganNoTimer S_POISONED+                      , CreateItem Nothing CGround ANY_ARROW timerNone+                      , CreateItem Nothing CGround STARTING_WEAPON timerNone+                      , reliefMsg, reliefMsg, reliefMsg+                      , reliefMsg, reliefMsg, reliefMsg ]]   , idesc    = "Broken chunks of foam concrete, glass and torn and burned equipment."   , ikit     = []   } doorwayTrapTemplate = ItemKind   { isymbol  = '+'   , iname    = "doorway trap"-  , ifreq    = [("doorway trap unknown", 1)]+  , ifreq    = [(DOORWAY_TRAP_UNKNOWN, 1), (DOORWAY_TRAP, 0)]+      -- the void group needed to pick the item for tile triggering+      -- even when not yet identified   , iflavour = zipPlain brightCol   , icount   = 1   , irarity  = [(1, 1)]   , iverbHit = "cripple"   , iweight  = 10000   , idamage  = 0-  , iaspects = [HideAs "doorway trap unknown"]+  , iaspects = [PresentAs DOORWAY_TRAP_UNKNOWN]       -- not Durable, springs at most once   , ieffects = []   , idesc    = "Just turn the handle..."   , ikit     = []   } doorwayTrap1 = doorwayTrapTemplate-  { ifreq    = [("doorway trap", 50)]-  , ieffects = [toOrganBad "blind" $ (1 `dL` 4) * 5]-  -- , idesc    = ""+  { ifreq    = [(DOORWAY_TRAP, 50)]+  , ieffects = [toOrganBad S_BLIND $ (1 `dL` 4) * 5]+  -- , idesc    = ""  -- TODO: once physical mechanism decided, also add ways+                      -- to disarm it, using up some items for that   } doorwayTrap2 = doorwayTrapTemplate-  { ifreq    = [("doorway trap", 25)]-  , ieffects = [toOrganBad "slowed" $ (1 `dL` 4) * 10]+  { ifreq    = [(DOORWAY_TRAP, 25)]+  , ieffects = [toOrganBad S_SLOWED $ (1 `dL` 4) * 10]   -- , idesc    = ""   } doorwayTrap3 = doorwayTrapTemplate-  { ifreq    = [("doorway trap", 25)]-  , ieffects = [toOrganBad "weakened" $ (1 `dL` 4) * 10 ]+  { ifreq    = [(DOORWAY_TRAP, 25)]+  , ieffects = [toOrganBad S_WEAKENED $ (1 `dL` 4) * 10 ]   -- , idesc    = ""   } stairsUp = ItemKind   { isymbol  = '<'   , iname    = "flight"-  , ifreq    = [("staircase up", 1)]+  , ifreq    = [(STAIRS_UP, 1)]   , iflavour = zipPlain [BrWhite]   , icount   = 1   , irarity  = [(1, 1)]@@ -256,56 +428,57 @@   , idamage  = 0   , iaspects = [ELabel "of steps", SetFlag Durable]   , ieffects = [Ascend True]-  , idesc    = "Stairs that rise towards the spaceship core."+  , idesc    = "Stairs that rise towards the spaceship core. It takes slightly more effort to climb than descend, but in lowered gravity it's almost instantaneous in either case."   , ikit     = []   } stairsDown = stairsUp   { isymbol  = '>'-  , ifreq    = [("staircase down", 1)]+  , ifreq    = [(STAIRS_DOWN, 1)]   , ieffects = [Ascend False]-  , idesc    = "Stairs that descend towards the outer ring."+  , idesc    = "Stairs that descend towards the outer ring. Narrow enough that only one person can comfortably use them at a time, but short enough that the whole team may climb down in quick succession."   } escape = stairsUp-  { isymbol  = 'E'+  { isymbol  = '>'   , iname    = "way"-  , ifreq    = [("escape", 1)]-  , iflavour = zipPlain [BrYellow]+  , ifreq    = [(ESCAPE, 1)]+  , iflavour = zipPlain [BrGreen]   , iaspects = [SetFlag Durable]   , ieffects = [Escape]   , idesc    = "May this nightmare have an end?"                  -- generic escape, so the text should be too;                  -- for moon outdoors, spaceship, everywhere   }-staircaseTrapUp = ItemKind+stairsTrapUp = ItemKind   { isymbol  = '^'   , iname    = "staircase trap"-  , ifreq    = [("staircase trap up", 1)]-  , iflavour = zipPlain [Red]+  , ifreq    = [(STAIRS_TRAP_UP, 1)]+  , iflavour = zipPlain [BrRed]   , icount   = 1   , irarity  = [(1, 1)]   , iverbHit = "buffet"   , iweight  = 10000   , idamage  = 0   , iaspects = []  -- not Durable, springs at most once-  , ieffects = [ VerbMsg "be caught in decompression blast"+  , ieffects = [ VerbMsgFail "be caught in decompression blast" "."                , Teleport $ 3 + 1 `dL` 10 ]   , idesc    = ""   , ikit     = []   }--- Needs to be separate from staircaseTrapUp, to make sure the item is--- registered after up staircase (not only after down staircase)+-- Needs to be separate from stairsTrapUp, to make sure the item is+-- registered after up stairs (not only after down stairs) -- so that effects are invoked in the proper order and, e.g., teleport works.-staircaseTrapDown = staircaseTrapUp-  { ifreq    = [("staircase trap down", 1)]+stairsTrapDown = stairsTrapUp+  { ifreq    = [(STAIRS_TRAP_DOWN, 1)]+  , iflavour = zipPlain [Red]   , iverbHit = "open up under"-  , ieffects = [ VerbMsg "tumble down the stairwell"-               , toOrganGood "drunk" (20 + 1 `d` 5) ]+  , ieffects = [ VerbMsgFail "fall down the stairwell" "."+               , toOrganGood S_DRUNK (20 + 1 `d` 5) ]   , idesc    = "A treacherous slab, to teach those who are too proud."   }-pulpit = ItemKind+lectern = ItemKind   { isymbol  = '?'   , iname    = "VR harness"-  , ifreq    = [("pulpit", 1)]+  , ifreq    = [(LECTERN, 1)]   , iflavour = zipFancy [BrYellow]   , icount   = 1   , irarity  = [(1, 1)]@@ -313,18 +486,18 @@   , iweight  = 10000   , idamage  = 0   , iaspects = []  -- not Durable, springs at most once-  , ieffects = [ OneOf [ CreateItem CGround "any scroll" timerNone+  , ieffects = [ OneOf [ CreateItem Nothing CGround ANY_SCROLL timerNone                        , Detect DetectAll 20                        , Paralyze $ (1 `dL` 6) * 10-                       , toOrganGood "drunk" (20 + 1 `d` 5) ]-               , Explode "story-telling" ]+                       , toOrganGood S_DRUNK (20 + 1 `d` 5) ]+               , Explode STORY_TELLING ]   , idesc    = ""   , ikit     = []   } shallowWater = ItemKind   { isymbol  = '~'   , iname    = "shallow water"-  , ifreq    = [("shallow water", 1)]+  , ifreq    = [(SHALLOW_WATER, 1)]  -- may be too shallow to be source   , iflavour = zipFancy [BrCyan]   , icount   = 1   , irarity  = [(1, 1)]@@ -332,14 +505,14 @@   , iweight  = 10000   , idamage  = 0   , iaspects = [SetFlag Durable]-  , ieffects = [ParalyzeInWater 2]-  , idesc    = ""+  , ieffects = [ParalyzeInWater 2, OnCombine waterEffect]+  , idesc    = "Slows down movement. Essential when sharpening weapons."   , ikit     = []   } straightPath = ItemKind-  { isymbol  = '.'+  { isymbol  = floorSymbol   , iname    = "straight path"-  , ifreq    = [("straight path", 1)]+  , ifreq    = [(STRAIGHT_PATH, 1)]   , iflavour = zipFancy [BrRed]   , icount   = 1   , irarity  = [(1, 1)]@@ -352,11 +525,11 @@   , ikit     = []   } frozenGround = ItemKind-  { isymbol  = '.'+  { isymbol  = floorSymbol   , iname    = "shade"-  , ifreq    = [("frozen ground", 1)]+  , ifreq    = [(FROZEN_GROUND, 1)]   , iflavour = zipFancy [BrBlue]-  , icount   = 50  -- very thick ice and refreezes+  , icount   = 10  -- very thick ice and refreezes, but not too large and boring   , irarity  = [(1, 1)]   , iverbHit = "betray"   , iweight  = 10000@@ -370,10 +543,18 @@  -- * Allure-specific +stairsUpOutdoor = stairsUp+  { ifreq    = [(STAIRS_UP_OUTDOOR, 1)]+  , idesc    = "Stairs that rise towards the sky. It takes slightly more effort to climb than descend, but in the low gravity it's almost instantaneous in either case."+  }+stairsDownOutdoor = stairsDown+  { ifreq    = [(STAIRS_DOWN_OUTDOOR, 1)]+  , idesc    = "Stairs that descend towards the underground. Narrow enough that only one person can comfortably use them at a time, but short enough that the whole team may climb down in quick succession."+  } blackStarrySky = ItemKind   { isymbol  = ' '   , iname    = "black starry sky"-  , ifreq    = [("black starry sky", 1)]+  , ifreq    = [(BLACK_STARRY_SKY, 1)]   , iflavour = zipPlain [Black]   , icount   = 1   , irarity  = [(1, 1)]@@ -381,15 +562,15 @@   , iweight  = 1000   , idamage  = 0   , iaspects = [SetFlag Durable]-  , ieffects = [ VerbMsg "look into the void and it looks back"-               , OneOf [RefillCalm 5, RefillCalm (-5)] ]-  , idesc    = "Occasionally a planet or the Sun zips by, but is unable to disperse the darkness. The black starscape constantly rotates. The frantic dance is silent, muted, indifferent. There is not even a hint of vibration, just the sense of heaviness and dizziness."  -- appears only on 100% flavour tiles, useless and trivial to notice, so the writeup can be longer; who am I kidding, I can't make myself write condensed prose+  , ieffects = [ VerbMsg "look into the void and it looks back" "."+               , RefillCalm (-5) ]+  , idesc    = "Occasionally a planet or the Sun zips by, but is unable to disperse the darkness. The black starscape constantly rotates. The frantic dance is silent, muted, indifferent. There is not even a hint of vibration, just the sense of heaviness and dizziness."  -- appears only on 100% flavour tiles (both floor and walls on some levels), useless and trivial to notice, so the writeup can be longer; who am I kidding, I can't make myself write condensed prose   , ikit     = []   } disengagedDocking = ItemKind-  { isymbol  = '<'+  { isymbol  = '>'   , iname    = "disengaged docking gear"-  , ifreq    = [("disengaged docking", 1)]+  , ifreq    = [(DISENGAGED_DOCKING_GEAR, 1)]   , iflavour = zipPlain [BrBlack]   , icount   = 1   , irarity  = [(1, 1)]@@ -397,14 +578,32 @@   , iweight  = 10000   , idamage  = 0   , iaspects = [SetFlag Durable]-  , ieffects = [RefillCalm (-10)]-  , idesc    = "Unfortunately this airlock has no space boat attached. Many fine small craft were originally docked with such sockets and clamps, but after the spaceship spontaneously deorbited Neptune, a lot of them were seen jettisoned and drifting astern. What a waste."+  , ieffects = []+  , idesc    = "After a short examination it's clear this is not the airlock you arrived through. In fact, this airlock has no space boat attached at all. Many fine small craft were originally docked with such sockets and clamps, but after the spaceship spontaneously deorbited Neptune, a lot of them were seen jettisoned and drifting astern. What a waste. It seems the decks up, closer to the ship's core, have not been purged of shuttles as thoroughly.\nThe airlock still works, but to get out onto the outer hull surface, you'd need a spacesuit with an air tank."   , ikit     = []   }+desertedAirlock = ItemKind+  { isymbol  = '>'+  , iname    = "the Initial Entrance"+  , ifreq    = [(DISENGAGED_DOCKING_GEAR, 10000)]+  , iflavour = zipPlain [Green]+  , icount   = 1+  , irarity  = [(1, 1), (2, 0)]+  , iverbHit = "worry"+  , iweight  = 10000+  , idamage  = 0+  , iaspects = [SetFlag Unique, SetFlag Durable]+  , ieffects = []+  , idesc    = "You recognize the inner airlock door smeared with guano in a familiar pattern, but you can't see the shuttle you left engaged to the airlock clamps outside. That's a chilling realization: your craft was among those shed by the spaceship. Now you have to find another lifeboat, likely on a deck high up towards the spaceship core, to which you now have to break through.\nMoreover, something's gone through your supplies, chewing, tearing and scattering most of them. Given that your stay just got prolonged, the shortage of food is almost as alarming as the scarcity of nano first aid vials that could patch the wounds from your recent struggles, until you are able to undergo a proper slow reconstruction in a hospital."+  , ikit     = [ (COOKED_FOOD, CGround), (COOKED_FOOD, CGround)+               , (S_SPACESUIT_TORN, CGround)+               , (S_SHORT_BLUNT_HAMMER, CGround)+               , (S_CROWBAR, CGround) ]+  } ruinedFirstAidKit = ItemKind   { isymbol  = '?'   , iname    = "ruined first aid kit"-  , ifreq    = [("ruined first aid kit", 1)]+  , ifreq    = [(RUINED_FIRST_AID_KIT, 1)]   , iflavour = zipPlain [BrGreen]   , icount   = 1   , irarity  = [(1, 1)]@@ -412,77 +611,210 @@   , iweight  = 1000   , idamage  = 0   , iaspects = []  -- not Durable, springs at most once-  , ieffects = [ VerbMsg "inspect a tattered CPR instruction soaked in a residue of oily drugs"-               , OneOf [ toOrganNoTimer "poison resistant"-                       , toOrganNoTimer "slow resistant"-                       , toOrganGood "drunk" (20 + 1 `d` 5) ]-               , CreateItem CInv "needle" timerNone ]+  , ieffects = [ VerbMsg "inspect a tattered CPR instruction soaked in a residue of oily drugs" "."+               , OneOf [ toOrganNoTimer S_SLOW_RESISTANT+                       , toOrganNoTimer S_POISON_RESISTANT+                       , toOrganGood S_DRUNK (20 + 1 `d` 5) ]+               , CreateItem Nothing CGround NEEDLE timerNone ]   , idesc    = ""  -- regulations require; say HP not regenerated in the game; mention how to regain HP   , ikit     = []   }+fireFightingGear = ItemKind+  { isymbol  = '?'+  , iname    = "fire fighting gear"+  , ifreq    = [(FIRE_FIGHTING_GEAR, 1), (WATER_SOURCE, 1)]+  , iflavour = zipPlain [BrRed]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "douse"+  , iweight  = 1000+  , idamage  = 0+  , iaspects = []  -- not Durable, springs at most once+  , ieffects = [ VerbMsg "disassemble and sort through the broken and leaking gear, taking away the least decrepit item" "."+               , CreateItem Nothing CGround FIRE_FIGHTING_ITEM timerNone ]+  , idesc    = "In addition to remains of firefighting tools, it contains a fire hydrant displaying old scars from being used in a hurry."  -- regulations require; hint that terrain can be ignited and doused+  , ikit     = []+  }+fireFightingGearIntact = ItemKind+  { isymbol  = '?'+  , iname    = "the Fire Fighting Set"+  , ifreq    = [(FIRE_FIGHTING_GEAR, 1), (WATER_SOURCE, 1)]+  , iflavour = zipPlain [Red]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "douse"+  , iweight  = 1000+  , idamage  = 0+  , iaspects = [SetFlag Unique]  -- not Durable, springs at most once+  , ieffects = [ VerbMsg "disassemble and sort through the old gear, taking away the least decrepit items" "."+               , CreateItem Nothing CGround FIRE_FIGHTING_ITEM timerNone+               , CreateItem Nothing CGround S_FIRE_AXE timerNone ]+  , idesc    = "This cabinet has not been broken open and used, so it contains a complete assortment of fire fighting implements. It also contains a fire hydrant in pristine condition."  -- regulations require; hint that terrain can be ignited and doused+  , ikit     = []+  } wall3dBillboard = ItemKind   { isymbol  = '*'   , iname    = "3D display"-  , ifreq    = [("3D display", 1)]-  , iflavour = zipPlain [BrGreen]+  , ifreq    = [(DISPLAY_3D, 1)]+  , iflavour = zipPlain [BrBlue]   , icount   = 1   , irarity  = [(1, 1)]   , iverbHit = "push"   , iweight  = 1000   , idamage  = 0   , iaspects = [Timeout 3, SetFlag Durable]-  , ieffects = [ VerbMsg "make it cough up a wobbly standalone hologram once more"-               , OneOf [ Explode "advertisement"-                       , Explode "story-telling" ] ]+  , ieffects = [ VerbMsg "make it cough up a wobbly standalone hologram once more" "."+               , OneOf [ Explode ADVERTISEMENT+                       , Explode STORY_TELLING ] ]   , idesc    = "One can still make out excited moves of bleached shapes."   , ikit     = []   }+crackedFlue = ItemKind+  { isymbol  = '|'+  , iname    = "cracked flue"+  , ifreq    = [(CRACKED_FLUE, 1)]  -- TODO: ("methane source", 1)?+  , iflavour = zipPlain [BrBlack]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "blow"+  , iweight  = 1000+  , idamage  = 0+  , iaspects = [Timeout 10, SetFlag Durable]+  , ieffects = [ VerbMsg "imagine the fragrance of roasted food wafting through the flue from upstairs" "."+               , OneOf [ toOrganGood S_KEEN_SMELLING (3 + 1 `d` 2)+                       , CreateItem Nothing CGround STEEL_SCRAP timerNone ] ]+  , idesc    = "The pipes ring with tumultuous echoes. Whenever you convince yourself it's an uneven updraft singing through the cracks, the noise suddenly stops, then picks up with a roar. Is there a fight over the food on some upper deck or are you just hungry?"+  , ikit     = []+  }+bloodOnWall = ItemKind+  { isymbol  = ','+  , iname    = "blotch"+  , ifreq    = [(BLOOD_ON_WALL, 60)]+  , iflavour = zipPlain [BrRed]+  , icount   = 1 `d` 6+  , irarity  = [(1, 1)]+  , iverbHit = "stain"+  , iweight  = 1+  , idamage  = 0+  , iaspects = [ELabel "of red fluid"]+  , ieffects = [RefillCalm (-5)]+  , idesc    = ""  -- probably enough said+  , ikit     = []+  }+bloodOnWall2 = bloodOnWall+  { ifreq    = [(BLOOD_ON_WALL, 30)]+  , iflavour = zipPlain [BrGreen]+  , iaspects = [ELabel "of green fluid"]+  , ieffects = [RefillCalm (-10)]+  }+bloodOnWall3 = bloodOnWall+  { ifreq    = [(BLOOD_ON_WALL, 10)]+  , iflavour = zipPlain [BrBlue]+  , iaspects = [ELabel "of blue fluid"]+  , ieffects = [RefillCalm (-15)]+  } depositBox = treasureCache   { iname    = "intact deposit box"-  , ifreq    = [("deposit box", 1)]+  , ifreq    = [(DEPOSIT_BOX, 60)]   , iaspects = [SetFlag Durable]-  , ieffects = [CreateItem CGround "treasure" timerNone]-                 -- can't be "valuable" or template items generated+  , ieffects = [CreateItem Nothing CGround COMMON_ITEM timerNone]+                 -- can't be VALUABLE or template items generated   , idesc    = "The reports of intact deposit boxes in the ship's safes have been greatly exaggerated, but there are still a few with glittering gems and gold, just waiting to be taken. Whomever looted these halls wasn't thorough or, judging from the damage to some of the boxes, was in an extreme hurry."   }+depositBoxSummonHero = treasureCache+  { iname    = "the Noisy Large Safe"+  , ifreq    = [(DEPOSIT_BOX, 30)]+  , iflavour = zipFancy [Blue]+  , iaspects = [SetFlag Unique]+  , ieffects = [Summon HERO 1, CreateItem Nothing CGround MUSEAL timerNone]+  , idesc    = "Judging from the frantic sounds, this deposit cell with a time lock has trapped something big recently."+  }+depositBoxSummonMonster = treasureCache+  { iname    = "throbbing deposit box"+  , ifreq    = [(DEPOSIT_BOX, 10)]+  , iflavour = zipFancy [Red]+  , iaspects = []+  , ieffects = [ Summon MOBILE_MONSTER 1+               , CreateItem Nothing CGround TREASURE timerNone ]+  , idesc    = "Judging from the incessant noises, this deposit cell with a time lock has trapped something recently."+  } jewelryCase = treasureCache-  { iname    = "jewelry case"-  , ifreq    = [("jewelry case", 1)]+  { iname    = "reinforced glass case"+  , ifreq    = [(JEWELRY_CASE, 1)]   , iaspects = [SetFlag Durable]-  , ieffects = [CreateItem CGround "any jewelry" timerNone]+  , ieffects = [CreateItem Nothing CGround ANY_JEWELRY timerNone]   , idesc    = "The customers of these shops must have been extremely well off, judging from abundance and quality of the jewelry, often extremely valuable in each of the artistic, material and nanotechnology aspects. Outer Solar System trips are expensive, but they offer unique trade and investment opportunities. Many deals are of the kind that can only be negotiated in a sealed room out of reach of satellites and screened by both parties. Among the jewelry are portable versions of such screening hardware --- in a truly breathtaking package."   }+ediblePlantRipe = treasureCache+  { iname    = "edible vegetation"+  , ifreq    = [(EDIBLE_PLANT_RIPE, 1)]+  , iflavour = zipPlain [Green]+  , iaspects = [SetFlag Durable]+  , ieffects = [CreateItem Nothing CGround EDIBLE_PLANT timerNone]+  , idesc    = ""+  }+stairsTrapDownOil = stairsTrapUp+  { ifreq    = [(STAIRS_TRAP_DOWN_OIL, 1)]+  , iflavour = zipPlain [Brown]+  , iverbHit = "cause a chaotic skid"+  , ieffects = [ VerbMsgFail "tumble down and shoot out of the stairwell" "."+               , PushActor (ThrowMod 400 100 1)]  -- 4 steps, 2 turns+  , idesc    = ""+  }+doorTrapPush = doorwayTrapTemplate+  { isymbol  = '+'+  , iname    = "weak door frame"+  , ifreq    = [(DOOR_TRAP_PUSH, 1)]+  , iflavour = zipPlain [Blue]+  , iverbHit = "give in"+  , iaspects = []  -- identified; not Durable, springs at most once+  , ieffects = [ VerbMsgFail "fly inwards after the crashed open doors" "."+               , PushActor (ThrowMod 400 100 1)]  -- 4 steps, 2 turns+  , idesc    = ""+  } liftUp = stairsUp   { iname    = "carriage"-  , ifreq    = [("lift up", 1)]+  , iflavour = zipPlain [Blue]+  , ifreq    = [(LIFT_UP, 1)]   , iaspects = [SetFlag Durable]   , idesc    = ""  -- describe inner levels of the ship   } liftDown = stairsDown   { iname    = "carriage"-  , ifreq    = [("lift down", 1)]+  , iflavour = zipPlain [Blue]+  , ifreq    = [(LIFT_DOWN, 1)]   , iaspects = [SetFlag Durable]   , idesc    = ""  -- describe outer levels of the ship   }-liftTrap = staircaseTrapUp+liftTrap = stairsTrapUp   { iname    = "elevator trap"  -- hat tip to US heroes-  , ifreq    = [("lift trap", 100)]+  , ifreq    = [(LIFT_TRAP, 100)]+  , iflavour = zipPlain [Cyan]   , iverbHit = "squeeze"-  , ieffects = [ VerbMsg "be crushed by the sliding doors"-               , DropBestWeapon, Paralyze 10 ]+  , ieffects = [ VerbMsgFail "be crushed by the sliding doors" "."+               , DropItem maxBound 1 CEqp STARTING_WEAPON, Paralyze 10 ]   , idesc    = ""   } liftTrap2 = liftTrap-  { ifreq    = [("lift trap", 50)]+  { ifreq    = [(LIFT_TRAP, 50)]+  , iflavour = zipFancy [BrMagenta]   , iverbHit = "choke"-  , ieffects = [ VerbMsg "inhale the gas lingering inside the cab"-               , toOrganBad "slowed" $ (1 `dL` 4) * 10 ]+  , ieffects = [ VerbMsgFail "inhale the gas lingering inside the cab" "."+               , toOrganBad S_SLOWED $ (1 `dL` 4) * 10 ]   , idesc    = ""   }+liftTrap3 = liftTrap+  { ifreq    = [(LIFT_TRAP, 50)]+  , iflavour = zipFancy [BrBlue]+  , iverbHit = "shock"+  , ieffects = [ VerbMsgFail "be electrocuted upon touching the control pad" "."+               , Discharge 5 $ 80 - 1 `d` 40 ]+  , idesc    = ""+  } shuttleHardware = ItemKind   { isymbol  = '#'   , iname    = "shuttle hardware"-  , ifreq    = [("shuttle hardware", 1)]+  , ifreq    = [(SHUTTLE_HARDWARE, 1)]   , iflavour = zipPlain [BrWhite]   , icount   = 1   , irarity  = [(1, 1)]@@ -495,17 +827,17 @@   , ikit     = []   } machineOil = ItemKind-  { isymbol  = '!'+  { isymbol  = '~'   , iname    = "oil layer"-  , ifreq    = [("machine oil", 1)]+  , ifreq    = [(OIL_PUDDLE, 1), (OIL_SOURCE, 1)]   , iflavour = zipPlain [BrYellow]-  , icount   = 3  -- not durable, wears off+  , icount   = 5  -- not durable, wears off   , irarity  = [(1, 1)]   , iverbHit = "oil"   , iweight  = 1000   , idamage  = 0   , iaspects = []-  , ieffects = [PushActor (ThrowMod 600 10 1)]+  , ieffects = [PushActor (ThrowMod 600 10 1), OnCombine oilEffect]                   -- the high speed represents gliding rather than flying                   -- and so no need to lift actor's weight off the ground;                   -- low linger comes from abrupt halt over normal surface@@ -515,22 +847,22 @@ crudeWeld = ItemKind  -- this is also an organ   { isymbol  = '_'   , iname    = "crude weld"-  , ifreq    = [("crude weld", 1)]+  , ifreq    = [(S_CRUDE_WELD, 1)]   , iflavour = zipPlain [BrMagenta]-  , icount   = 1  -- not durable, destroyed on activation+  , icount   = 1   , irarity  = [(1, 1)]   , iverbHit = "weld"   , iweight  = 3000   , idamage  = 0-  , iaspects = [AddSkill SkMove (-5), AddSkill SkDisplace (-1)]-  , ieffects = [Explode "spark"]-  , idesc    = "This is a messy and irregularly layered weld, but no amount of kicking nor hammering makes any impression on it. A heavy duty cutting tool would be required."+  , iaspects = [AddSkill SkMove (-5), AddSkill SkDisplace (-1), SetFlag Durable]+  , ieffects = [Explode S_SPARK]+  , idesc    = "Such a superfluous mass of molten metal was layered on with messy welds that no amount of kicking nor hammering has any effect. A heavy duty cutting tool would be required or skilled thermal cycling. Whomever did the melding may still be around, but convincing him to yield his fiery implements may be a dangerous endeavour."   , ikit     = []   } decontaminator = ItemKind-  { isymbol  = 'o'+  { isymbol  = 'D'   , iname    = "decontamination chamber"-  , ifreq    = [("decontamination chamber", 1)]+  , ifreq    = [(DECONTAMINATION_CHAMBER, 1)]   , iflavour = zipPlain [BrBlue]   , icount   = 1   , irarity  = [(1, 1)]@@ -538,18 +870,238 @@   , iweight  = 500000   , idamage  = 0   , iaspects = [SetFlag Durable]-  , ieffects = [ DropItem 1 1 COrgan "genetic flaw"-               , DropItem maxBound maxBound CEqp "common item"-               , DropItem maxBound maxBound CInv "common item"-               , DropItem maxBound maxBound CSha "common item"-               , DropItem maxBound maxBound CEqp "curious item"-               , DropItem maxBound maxBound CInv "curious item"-               , DropItem maxBound maxBound CSha "curious item"-               , DropItem maxBound maxBound CEqp "treasure"-               , DropItem maxBound maxBound CInv "treasure"-               , DropItem maxBound maxBound CSha "treasure"-               , toOrganGood "rose-smelling" (20 + 1 `d` 5)+  , ieffects = [ DropItem 1 1 COrgan GENETIC_FLAW+               , DropItem maxBound maxBound CEqp COMMON_ITEM+               , DropItem maxBound maxBound CStash COMMON_ITEM+               , DropItem maxBound maxBound CEqp CRAWL_ITEM+               , DropItem maxBound maxBound CStash CRAWL_ITEM+               , DropItem maxBound maxBound CEqp TREASURE+               , DropItem maxBound maxBound CStash TREASURE+                   -- With movable shared stash location this puzzle now has+                   -- more solutions, including one for a lone wolf.+               , toOrganGood S_ROSE_SMELLING (20 + 1 `d` 5)                ]   , idesc    = "The area is under quarantine. No departure is permitted without decontamination. Personal belongings are to be decontaminated separately."   , ikit     = []   }+barrelFuel = ItemKind+  { isymbol  = 'b'+  , iname    = "fuel"+  , ifreq    = [(BARREL_CONTENTS, 20), (OIL_SOURCE, 1)]+  , iflavour = zipPlain [BrYellow]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "block"+  , iweight  = 100000+  , idamage  = 0+  , iaspects = []+  , ieffects = [Explode S_FOCUSED_BURNING_OIL_3, OnCombine oilEffect]+  , idesc    = ""+  , ikit     = []+  }+barrelFertilizer = barrelFuel+  { iname    = "fertilizer"+  , ifreq    = [(BARREL_CONTENTS, 30), (FIRE_SOURCE, 1)]+  , iflavour = zipPlain [Red]+  , ieffects = [Explode S_FOCUSED_FRAGMENTATION, OnCombine roastEffect5]+                 -- no S_FOCUSED_CONCUSSION; a barrel would destroy the ship;+                 -- no water barrel either, basins and running water in taps;+                 -- no VIOLENT variants of the blasts or bumping a lone+                 -- barrel would be safe+  , idesc    = ""+  }+barrelOxidizer = barrelFuel+  { iname    = "oxidizer"+  , ifreq    = [(BARREL_CONTENTS, 20), (FIRE_SOURCE, 1)]+  , iflavour = zipPlain [BrWhite]+  , ieffects = [Explode S_FOCUSED_FLASH, OnCombine roastEffect5]+  , idesc    = ""+  }+barrelOil = barrelFuel+  { iname    = "lubricant oil"+  , ifreq    = [(BARREL_CONTENTS, 20), (OIL_SOURCE, 1)]+  , iflavour = zipPlain [Brown]+  , ieffects = [Explode S_MELEE_PROTECTIVE_BALM, OnCombine oilEffect]+                 -- beneficial, so OK not to affect the triggering actor+  , idesc    = ""+  }+barrelNitrogen = barrelFuel+  { iname    = "liquid nitrogen"+  , ifreq    = [(BARREL_CONTENTS, 40), (COLD_SOURCE, 1)]+  , iflavour = zipPlain [BrBlack]+  , ieffects = [Explode S_FOCUSED_SLOWNESS_MIST]+                 -- may exploit to harm foes; watch out for friends+  , idesc    = ""+  }+workshopBench = ItemKind+  { isymbol  = ':'+  , iname    = "bench"+  , ifreq    = [(WORKSHOP_BENCH, 1)]+  , iflavour = zipPlain [BrBlue]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "bury"+  , iweight  = 100000+  , idamage  = 0+  , iaspects = [SetFlag Durable]+  , ieffects = [OnCombine workshopEffect]+  , idesc    = "A sturdy table with an anvil, a vice and an overhang of pipes, tubes, wires and probes. Place the components and tools adjacent to the table, stand over them and craft by trying to modify the bench with the 'M' command."+  , ikit     = []+  }+signageExitLuggage = signageExit+  { irarity  = [(1, 1), (2, 0)]+  , idesc    = idesc signageExit+               <+> "If exits blocked, luggage claim area marked with 'L' is the concentration point."+  }+signageEmbedLuggage = signageEmbed+  { irarity  = [(1, 1), (2, 0)]+  , idesc    = idesc signageEmbed+               <+> "Keys and tools are held in the luggage claim booth marked with 'L'."+  }+signageMerchandiseLuggage = signageMerchandise+  { irarity  = [(1, 1), (2, 0)]+  , idesc    = idesc signageMerchandise+               <+> "Queries and complaints can be filed to the port officer and luggage attendant this way under the 'L' sign."+  }++combineEffect :: Text -> [( [(Int, GroupName ItemKind)]+                          , [(Int, GroupName ItemKind)]+                          , [(Int, GroupName ItemKind)] )]+              -> Effect+combineEffect msg ass =+  let createOne :: (Int, GroupName ItemKind) -> Effect+      createOne (count, grp) = CreateItem (Just count) CGround grp timerNone+      createList :: [(Int, GroupName ItemKind)] -> Effect+      createList = SeqEffect . map createOne+      cookOne :: ( [(Int, GroupName ItemKind)]+                 , [(Int, GroupName ItemKind)]+                 , [(Int, GroupName ItemKind)] )+              -> Effect+      cookOne (tools, raw, cooked) =+        ConsumeItems tools raw  -- either all destroyed or none+        `AndEffect`  -- either destroy and create, or none+        createList cooked+      f :: ( [(Int, GroupName ItemKind)]+           , [(Int, GroupName ItemKind)]+           , [(Int, GroupName ItemKind)] )+        -> Effect+        -> Effect+      f roolsRawCooked eff = cookOne roolsRawCooked `OrEffect` eff+      initial = VerbMsgFail msg "."  -- noop; emits @UseId@ to correctly abort+  in foldr f initial ass++oilEffect :: Effect+oilEffect = combineEffect "have nothing to oil"+              [ ( [], [(1, S_STAFF), (1, THICK_CLOTH)]+                , [(1, S_DOUSED_WOODEN_TORCH)] )+              , ( [], [(1, S_EMPTY_FLASK), (1, THICK_CLOTH)]+                , [(1, S_DOUSED_OIL_LAMP)] ) ]++roastEffect :: Effect+roastEffect = combineEffect "have nothing to roast"+              $ extraRoastAssocs+                ++ map (\(raw, cooked) ->+                          ([], [(1, raw)], [(1, cooked)])) cookingAssocs++roastEffect5 :: Effect+roastEffect5 = combineEffect "have nothing to roast"+               $ extraRoastAssocs+                 ++ concatMap (\(raw, cooked) ->+                                 [ ([], [(5, raw)], [(5, cooked)])+                                 , ([], [(4, raw)], [(4, cooked)])+                                 , ([], [(3, raw)], [(3, cooked)])+                                 , ([], [(2, raw)], [(2, cooked)])+                                 , ([], [(1, raw)], [(1, cooked)])+                                 ])+                              cookingAssocs++waterEffect :: Effect+waterEffect = combineEffect "lack a sharpening tool or a weapon to sharpen or an item to fill with water"+              $ map (\(tools, raw, cooked) ->+                      (tools, [(1, raw)], [(1, cooked)])) sharpeningAssocs+                ++ extraWaterAssocs++workshopEffect :: Effect+workshopEffect = combineEffect "have not enough tools and components"+                               workshopAssocs++cookingAssocs :: [(GroupName ItemKind, GroupName ItemKind)]+cookingAssocs =+  [ (RAW_MEAT_CHUNK, ROASTED_MEAT_CHUNK)+  , (S_ENCHANCED_BERRY, S_COOKED_BERRY)+  , (S_FRAYED_FUNGUS, S_COOKED_FUNGUS)+  , (S_THIC_LEAF, S_COOKED_LEAF)+  , (S_RECONFIGURED_FRUIT, S_COOKED_FRUIT)+  , (S_FRAGRANT_HERB, S_COOKED_HERB)+  , (S_DULL_FLOWER, S_COOKED_FLOWER)+  , (S_SPICY_BARK, S_COOKED_BARK)+  , (S_PUMPKIN, S_COOKED_PUMPKIN)+  ]++extraRoastAssocs :: [( [(Int, GroupName ItemKind)]+                     , [(Int, GroupName ItemKind)]+                     , [(Int, GroupName ItemKind)] )]+extraRoastAssocs =+  [ ([], [(1, S_DOUSED_WOODEN_TORCH)], [(1, S_WOODEN_TORCH)])+  , ([], [(1, S_DOUSED_OIL_LAMP)], [(1, S_OIL_LAMP)])+  , ([], [(1, WASTE_CONTAINER)], [(1, S_REFRIGERATION_COIL)])+  ]++sharpeningAssocs :: [( [(Int, GroupName ItemKind)]+                     , GroupName ItemKind+                     , GroupName ItemKind )]+sharpeningAssocs =+  [ ([(1, SHARPENING_TOOL)], S_HARPOON_CARGO, S_HARPOON_SHARP)+  , ([(1, SHARPENING_TOOL), (1, BREACHING_TOOL)], S_PIPE, S_SHARPENED_PIPE)+  , ([(1, SHARPENING_TOOL)], S_SHIELD_BLUNT, S_SHIELD_SHARP)+  , ([(2, SHARPENING_TOOL)], S_SHORT_BLUNT_HAMMER, S_SHORT_SHARP_HAMMER)+  , ([(2, SHARPENING_TOOL)], S_LONG_BLUNT_HAMMER, S_LONG_SHARP_HAMMER)+  , ([(2, SHARPENING_TOOL)], S_CLEAVER, S_DAGGER)+  , ([(1, SHARPENING_TOOL)], S_RAPIER_BLUNT, S_RAPIER_SHARP)+  , ([(2, SHARPENING_TOOL)], S_POLE_CLEAVER, S_LONG_SPEAR)+  , ([(1, SHARPENING_TOOL)], S_HALBERD_BLUNT, S_HALBERD_SHARP)+  ]++extraWaterAssocs :: [( [(Int, GroupName ItemKind)]+                     , [(Int, GroupName ItemKind)]+                     , [(Int, GroupName ItemKind)] )]+extraWaterAssocs =+  [ ( [(1, PERFUME)], [(5, S_EMPTY_FLASK)], [(5, S_ROSE_WATER_FLASK)] )+  , ( [], [(1, S_EMPTY_FLASK)], [(1, S_WATER_FLASK)] )+  ]++workshopAssocs :: [( [(Int, GroupName ItemKind)]+                   , [(Int, GroupName ItemKind)]+                   , [(Int, GroupName ItemKind)] )]+workshopAssocs =+  [ ( [(1, BONDING_TOOL)], [(1, POLE), (1, S_SHORT_BLUNT_HAMMER)]+    , [(1, S_LONG_BLUNT_HAMMER), (1, HANDLE)] )+  , ( [(1, BONDING_TOOL)], [(1, POLE), (1, S_SHORT_SHARP_HAMMER)]+    , [(1, S_LONG_SHARP_HAMMER), (1, HANDLE)] )+  , ( [(1, BONDING_TOOL)], [(1, POLE), (1, S_FIRE_AXE)]+    , [(1, S_POLL_AXE), (1, HANDLE)] )+  , ( [(2, BONDING_TOOL)], [(1, POLE), (1, S_CLEAVER)]+    , [(1, S_POLE_CLEAVER)] )+  , ( [(2, BONDING_TOOL)], [(1, POLE), (1, S_DAGGER)]+    , [(1, S_LONG_SPEAR)] )+  , ( [], [(1, S_STAFF), (1, STEEL_SCRAP)], [(1, S_SHORT_CLUB)] )+  , ( [], [(1, POLE), (2, STEEL_SCRAP)], [(1, S_LONG_CLUB)] )+  -- Perfect the perfection. Comes after the productive rules.+  , ( [(1, BONDING_TOOL)], [(1, S_LONG_SHARP_HAMMER)]+    , [(1, S_LONG_SHARP_HAMMER)] )+  , ( [(1, BONDING_TOOL)], [(1, S_POLL_AXE)], [(1, S_POLL_AXE)] )+  , ( [(1, BONDING_TOOL)], [(1, S_LONG_SPEAR)], [(1, S_LONG_SPEAR)] )+  , ( [(3, BONDING_TOOL)]+    , [ (1, S_SPACESUIT_JACKET), (1, S_SPACESUIT_TROUSERS)+      , (2, S_SPACESUIT_GLOVE), (1, S_SPACESUIT_HELMET)+      , (2, S_SPACESUIT_BOOT) ]+    , [(1, S_SPACESUIT)] )+  -- Recipes that destroy more useful things than other recipes come last.+  , ( [(1, WIRECUTTING_TOOL)], [(1, S_SPACESUIT_TORN)]+    , [(1, SPACESUIT_PART), (1, SPACESUIT_PART), (1, CLOTH_RAG)] )+  , ( [(1, PERFUME)], [(1, WATER_SOURCE), (5, S_EMPTY_FLASK)]+    , [(6, S_ROSE_WATER_FLASK)] )  -- the extra container is from water source+  , ( [(1, BREACHING_TOOL)], [(1, POLE_AND_STEEL)]  -- harder to dismantle+    , [(1, POLE), (1, STEEL_SCRAP)] )+  , ([], [(1, HANDLE_AND_STEEL)], [(1, HANDLE), (1, STEEL_SCRAP)])+  ]
GameDefinition/Content/ItemKindOrgan.hs view
@@ -1,604 +1,1147 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)--- This file is a part of the computer game Allure of the Stars--- and is released under the terms of the GNU Affero General Public License.--- For license and copyright information, see the file LICENSE.------ | Actor organ definitions.-module Content.ItemKindOrgan-  ( organs-  ) where--import Prelude ()--import Game.LambdaHack.Core.Prelude--import Game.LambdaHack.Content.ItemKind-import Game.LambdaHack.Core.Dice-import Game.LambdaHack.Definition.Ability-import Game.LambdaHack.Definition.Color-import Game.LambdaHack.Definition.Defs-import Game.LambdaHack.Definition.Flavour--organs :: [ItemKind]-organs =-  [fist, foot, hookedClaw, smallClaw, snout, smallJaw, jaw, largeJaw, antler, horn, rhinoHorn, tentacle, thorn, boilingFissure, arsenicFissure, sulfurFissure, beeSting, sting, venomTooth, venomFang, screechingBeak, largeTail, hugeTail, armoredSkin, bark, eye3, eye6, eye8, vision6, vision12, vision16, nostril, ear3, ear6, ear8, rattleOrgan, insectMortality, sapientBrain, animalBrain, speedGland5, speedGland10, scentGland, boilingVent, arsenicVent, sulfurVent, bonusHP, braced, asleep, impressed]-  -- Allure-specific-  ++ [smallBeak, razor, liveWire, robotBrain, hullPlating, mouthVent, geneticFlaw3, geneticFlaw10]--fist,    foot, hookedClaw, smallClaw, snout, smallJaw, jaw, largeJaw, antler, horn, rhinoHorn, tentacle, thorn, boilingFissure, arsenicFissure, sulfurFissure, beeSting, sting, venomTooth, venomFang, screechingBeak, largeTail, hugeTail, armoredSkin, bark, eye3, eye6, eye8, vision6, vision12, vision16, nostril, ear3, ear6, ear8, rattleOrgan, insectMortality, sapientBrain, animalBrain, speedGland5, speedGland10, scentGland, boilingVent, arsenicVent, sulfurVent, bonusHP, braced, asleep, impressed :: ItemKind--- Allure-specific-smallBeak, razor, liveWire, robotBrain, hullPlating, mouthVent, geneticFlaw3, geneticFlaw10 :: ItemKind---- Weapons---- * Human weapon organs--fist = ItemKind-  { isymbol  = ','-  , iname    = "fist"-  , ifreq    = [("fist", 100)]-  , iflavour = zipPlain [Red]-  , icount   = 2-  , irarity  = [(1, 1)]-  , iverbHit = "punch"-  , iweight  = 2000-  , idamage  = 4 `d` 1-  , iaspects = [SetFlag Durable, SetFlag Meleeable]-  , ieffects = []-  , idesc    = "Simple but effective."-  , ikit     = []-  }-foot = fist-  { iname    = "foot"-  , ifreq    = [("foot", 50)]-  , iverbHit = "kick"-  , idamage  = 4 `d` 1-  , idesc    = "A weapon you can still use if disarmed."-                 -- great example of tutorial hints inside a flavourful text-  }---- * Other weapon organs--hookedClaw = fist-  { iname    = "hooked claw"-  , ifreq    = [("hooked claw", 50)]-  , icount   = 2  -- even if more, only the fore claws used for fighting-  , iverbHit = "hook"-  , idamage  = 2 `d` 1-  , iaspects = Timeout (12 - 1 `dL` 3)-               : iaspects fist-  , ieffects = [toOrganBad "slowed" 2]-  , idesc    = "A curved talon."-  }-smallClaw = fist-  { iname    = "small claw"-  , ifreq    = [("small claw", 50)]-  , iverbHit = "slash"-  , idamage  = 2 `d` 1-  , idesc    = "A pearly spike."-  }-snout = fist-  { iname    = "snout"-  , ifreq    = [("snout", 10)]-  , icount   = 1-  , iverbHit = "bite"-  , idamage  = 2 `d` 1-  , idesc    = "Sensitive and wide-nostrilled."-  }-smallJaw = fist-  { iname    = "small jaw"-  , ifreq    = [("small jaw", 20)]-  , icount   = 1-  , iverbHit = "rip"-  , idamage  = 3 `d` 1-  , idesc    = "Filled with small, even teeth."-  }-jaw = fist-  { iname    = "jaw"-  , ifreq    = [("jaw", 20)]-  , icount   = 1-  , iverbHit = "rip"-  , idamage  = 5 `d` 1-  , idesc    = "Delivers a powerful bite."-  }-largeJaw = fist-  { iname    = "large jaw"-  , ifreq    = [("large jaw", 100)]-  , icount   = 1-  , iverbHit = "crush"-  , idamage  = 10 `d` 1-  , iaspects = [Timeout $ 2 + 1 `d` 2]  -- no effect, but limit raw damage-               ++ iaspects fist-  , idesc    = "Enough to swallow anything in a single gulp."-  }-antler = fist-  { iname    = "antler"-  , ifreq    = [("antler", 100)]-  , icount   = 2-  , iverbHit = "ram"-  , idamage  = 4 `d` 1-  , iaspects = [ Timeout $ 3 + (1 `d` 3) * 3-               , AddSkill SkArmorMelee 10 ]  -- bonus doubled-               ++ iaspects fist-  , ieffects = [PushActor (ThrowMod 100 50 1)]  -- 1 step, slow-  , idesc    = ""-  }-horn = fist-  { iname    = "horn"-  , ifreq    = [("horn", 100)]-  , icount   = 2-  , iverbHit = "impale"-  , idamage  = 5 `d` 1-  , iaspects = [ AddSkill SkHurtMelee 10-               , AddSkill SkArmorMelee 10 ]  -- bonus doubled-               ++ iaspects fist-  , idesc    = "Sharp and long, for defence or attack."-  }-rhinoHorn = fist-  { iname    = "ugly horn"  -- made of keratin, unlike real horns-  , ifreq    = [("rhino horn", 100)]-  , icount   = 1  -- single, unlike real horns-  , iverbHit = "gore"-  , idamage  = 5 `d` 1-  , iaspects = [Timeout 5, AddSkill SkHurtMelee 20]-               ++ iaspects fist-  , ieffects = [Impress, Yell]  -- the owner is a mid-boss, after all-  , idesc    = "Very solid, considering it has the same composition as fingernails."-  }-tentacle = fist-  { iname    = "tentacle"-  , ifreq    = [("tentacle", 50)]-  , icount   = 4-  , iverbHit = "slap"-  , idamage  = 4 `d` 1-  , idesc    = "Damp and dextrous."-  }-thorn = fist-  { iname    = "thorn"-  , ifreq    = [("thorn", 100)]-  , icount   = 2 + 1 `d` 3-  , iverbHit = "puncture"-  , idamage  = 2 `d` 1-  , iaspects = [SetFlag Meleeable]  -- not Durable-  , ieffects = [VerbNoLonger "be not so thorny any more"]-  , idesc    = "Sharp yet brittle."-  }-boilingFissure = fist-  { iname    = "fissure"-  , ifreq    = [("boiling fissure", 100)]-  , icount   = 5 + 1 `d` 5-  , iverbHit = "hiss at"-  , idamage  = 1 `d` 1-  , iaspects = [ AddSkill SkHurtMelee 20  -- decreasing as count decreases-               , SetFlag Meleeable ]  -- not Durable-  , ieffects = [ DropItem 1 1 COrgan "condition"  -- useful; limited-               , VerbNoLonger "widen the crack, releasing pressure" ]-  , idesc    = ""-  }-arsenicFissure = boilingFissure-  { iname    = "fissure"-  , ifreq    = [("biogas fissure", 100)]-  , icount   = 3 + 1 `d` 3-  , idamage  = 2 `d` 1-  , ieffects = [ toOrganBad "parsimonious" (5 + 1 `d` 3)-               -- weaken/poison, impacting intellectual abilities first-               , VerbNoLonger "become clogged with organic residue" ]-  , idesc    = ""-  }-sulfurFissure = boilingFissure-  { iname    = "fissure"-  , ifreq    = [("medbot fissure", 100)]-  , icount   = 2 + 1 `d` 2-  , idamage  = 0  -- heal not via (negative) idamage, for armour would block it-  , ieffects = [ RefillHP 5-               , VerbNoLonger "run out of nano medbot liquid" ]-  , idesc    = ""-  }-beeSting = fist-  { iname    = "bee sting"-  , ifreq    = [("bee sting", 100)]-  , icount   = 1-  , iverbHit = "sting"-  , idamage  = 0-  , iaspects = [ AddSkill SkArmorMelee 200, AddSkill SkArmorRanged 45-               , SetFlag Meleeable ]  -- not Durable-  , ieffects = [Paralyze 6, RefillHP 4]-                 -- no special message when runs out, because it's 1 copy-  , idesc    = "Painful, but beneficial."-  }-sting = fist-  { iname    = "sting"-  , ifreq    = [("sting", 100)]-  , icount   = 1-  , iverbHit = "inject"-  , idamage  = 1 `d` 1-  , iaspects = [Timeout $ 10 - 1 `dL` 4, AddSkill SkHurtMelee 40]-               ++ iaspects fist-  , ieffects = [toOrganBad "retaining" (3 + 1 `d` 3)]-  , idesc    = "Painful, debilitating and harmful."-  }-venomTooth = fist-  { iname    = "venom tooth"-  , ifreq    = [("venom tooth", 100)]-  , icount   = 2-  , iverbHit = "bite"-  , idamage  = 1 `d` 1-  , iaspects = Timeout (7 - 1 `dL` 3)-               : iaspects fist-  , ieffects = [toOrganBad "slowed" (3 + 1 `d` 3)]-  , idesc    = "A chilling numbness spreads from its bite."-  }-venomFang = fist-  { iname    = "venom fang"-  , ifreq    = [("venom fang", 100)]-  , icount   = 2-  , iverbHit = "bite"-  , idamage  = 0-  , iaspects = Timeout (10 - 1 `dL` 5)-               : iaspects fist-  , ieffects = [toOrganNoTimer "poisoned"]-  , idesc    = "Dripping with deadly venom."-  }-screechingBeak = fist-  { iname    = "screeching beak"-  , ifreq    = [("screeching beak", 100)]-  , icount   = 1-  , iverbHit = "peck"-  , idamage  = 3 `d` 1-  , iaspects = Timeout (7 - 1 `dL` 3)-               : iaspects fist-  , ieffects = [Summon "scavenger" $ 1 `dL` 3]-  , idesc    = "Both a weapon and a beacon, calling more scavengers to the meal."-  }-largeTail = fist-  { iname    = "large tail"-  , ifreq    = [("large tail", 50)]-  , icount   = 1-  , iverbHit = "knock"-  , idamage  = 7 `d` 1-  , iaspects = [Timeout $ 2 + 1 `d` 2, AddSkill SkHurtMelee 20]-               ++ iaspects fist-                 -- timeout higher, lest they regain push before closing again-  , ieffects = [PushActor (ThrowMod 200 50 1)]  -- 1 step, fast-  , idesc    = "Almost as long as the trunk."-  }-hugeTail = largeTail-  { iname    = "huge tail"-  , ifreq    = [("huge tail", 50)]-  , iverbHit = "upend"-  , iaspects = [Timeout $ 3 + 1 `d` 2, AddSkill SkHurtMelee 20]-               ++ iaspects fist-                 -- timeout higher, lest they regain push before closing again-  , ieffects = [PushActor (ThrowMod 400 50 1)]  -- 2 steps, fast-  , idesc    = "Slow but immensely heavy."-  }---- Non-weapons---- * Armor organs--armoredSkin = ItemKind-  { isymbol  = ','-  , iname    = "armored skin"-  , ifreq    = [("armored skin", 100)]-  , iflavour = zipPlain [Red]-  , icount   = 1-  , irarity  = [(1, 1)]-  , iverbHit = "bash"-  , iweight  = 2000-  , idamage  = 0-  , iaspects = [ AddSkill SkArmorMelee 30, AddSkill SkArmorRanged 15-               , SetFlag Durable ]-  , ieffects = []-  , idesc    = "Homemade armour is just as good."  -- hmm, it may get confused with leather armor jackets, etc.-  , ikit     = []-  }-bark = armoredSkin-  { iname    = "bark"-  , ifreq    = [("bark", 100)]-  , idesc    = ""-  }---- * Sense organs--eye :: Int -> ItemKind-eye n = armoredSkin-  { iname    = "eye"-  , ifreq    = [(toGroupName $ "eye" <+> tshow n, 100)]-  , icount   = 2-  , iverbHit = "glare at"-  , iaspects = [ AddSkill SkSight (intToDice n)-               , SetFlag Durable ]-  , idesc    = "A piercing stare."-  }-eye3 = eye 3-eye6 = eye 6-eye8 = eye 8-vision :: Int -> ItemKind-vision n = armoredSkin-  { iname    = "vision"-  , ifreq    = [(toGroupName $ "vision" <+> tshow n, 100)]-  , iverbHit = "visualize"-  , iaspects = [ AddSkill SkSight (intToDice n)-               , SetFlag Durable ]-  , idesc    = ""-  }-vision6 = vision 6-vision12 = vision 12-vision16 = vision 16-nostril = armoredSkin-  { iname    = "nostril"-  , ifreq    = [("nostril", 100)]-  , icount   = 2-  , iverbHit = "snuff"-  , iaspects = [ AddSkill SkSmell 1  -- times 2, from icount-               , SetFlag Durable ]-  , idesc    = ""-  }-ear   :: Int -> ItemKind-ear n = armoredSkin-  { iname    = "ear"-  , ifreq    = [(toGroupName $ "ear" <+> tshow n, 100)]-  , icount   = 2-  , iverbHit = "overhear"-  , iaspects = [ AddSkill SkHearing (intToDice n)-               , SetFlag Durable ]-  , idesc    = ""-  }-ear3 = ear 3-ear6 = ear 6-ear8 = ear 8---- * Assorted--rattleOrgan = armoredSkin-  { iname    = "rattle"-  , ifreq    = [("rattle", 100)]-  , iverbHit = "announce"-  , iaspects = [ Timeout $ 10 + (1 `d` 3) * 10  -- long, to limit spam-               , SetFlag Periodic, SetFlag Durable ]-  , ieffects = [Yell, RefillCalm 5]-  , idesc    = ""-  }-insectMortality = armoredSkin-  { iname    = "insect mortality"-  , ifreq    = [("insect mortality", 100)]-  , iverbHit = "age"-  , iaspects = [ AddSkill SkAggression 2  -- try to attack before you die-               , Timeout $ 30 + (1 `d` 3) * 10  -- die very slowly-               , SetFlag Periodic, SetFlag Durable ]-  , ieffects = [RefillHP (-1), Yell]-  , idesc    = ""-  }-sapientBrain = armoredSkin-  { iname    = "sapient brain"-  , ifreq    = [("sapient brain", 100)]-  , iverbHit = "outbrain"-  , iaspects = [AddSkill sk 1 | sk <- [SkMove .. SkApply]]-               ++ [AddSkill SkMove 4]  -- can move at once when waking up-               ++ [AddSkill SkAlter 4]  -- can use all stairs; dig rubble, ice-               ++ [AddSkill SkWait 2]  -- can brace and sleep-               ++ [AddSkill SkApply 1]  -- can use most items, not just foods-               ++ [SetFlag Durable]-  , idesc    = ""-  }-animalBrain = armoredSkin-  { iname    = "animal brain"-  , ifreq    = [("animal brain", 100)]-  , iverbHit = "blank"-  , iaspects = [AddSkill sk 1 | sk <- [SkMove .. SkApply]]-               ++ [AddSkill SkMove 4]  -- can move at once when waking up-               ++ [AddSkill SkAlter 2]  -- can use normal stairs; can't dig-               ++ [AddSkill SkWait 2]  -- can brace and sleep-               -- No @SkAppy@ bonus, so can only apply foods.-               ++ [AddSkill SkDisplace (-1)]  -- no melee tactics-               ++ [AddSkill SkMoveItem (-1)]  -- no item gathering-               ++ [AddSkill SkProject (-1)]  -- nor item flinging-               ++ [SetFlag Durable]-  , idesc    = ""-  }-speedGland :: Int -> ItemKind-speedGland n = armoredSkin-  { iname    = "speed gland"-  , ifreq    = [(toGroupName $ "speed gland" <+> tshow n, 100)]-  , iverbHit = "spit at"-  , iaspects = [ AddSkill SkSpeed $ intToDice n-               , Timeout $ intToDice (100 `div` n)-               , SetFlag Periodic, SetFlag Durable ]-  , ieffects = [RefillHP 1]-  , idesc    = ""-  }-speedGland5 = speedGland 5-speedGland10 = speedGland 10-scentGland = armoredSkin-  { iname    = "scent gland"-  , ifreq    = [("scent gland", 100)]-  , icount   = 10 + 1 `d` 3  -- runs out-  , iverbHit = "spray at"-  , iaspects = [ Timeout $ (1 `d` 3) * 10-               , SetFlag Periodic, SetFlag Fragile ]  -- not Durable-  , ieffects = [ VerbNoLonger "look spent"-               , ApplyPerfume-               , Explode "distressing odor" ]-                   -- keep explosion at the end to avoid the ambiguity of-                   -- "of ([foo explosion] of [bar])"-  , idesc    = ""-  }-boilingVent = armoredSkin-  { iname    = "vent"-  , ifreq    = [("boiling vent", 100)]-  , iflavour = zipPlain [BrBlue]-  , iverbHit = "menace"-  , iaspects = [ Timeout $ (2 + 1 `d` 3) * 5-               , SetFlag Periodic, SetFlag Durable ]-  , ieffects = [RefillHP 2, Explode "boiling water"]-  , idesc    = ""-  }-arsenicVent = armoredSkin-  { iname    = "vent"-  , ifreq    = [("biogas vent", 100)]-  , iflavour = zipPlain [BrGreen]-  , iverbHit = "menace"-  , iaspects = [ Timeout $ (2 + 1 `d` 3) * 5-               , SetFlag Periodic, SetFlag Durable ]-  , ieffects = [RefillHP 2, Explode "sparse shower"]-  , idesc    = ""-  }-sulfurVent = armoredSkin-  { iname    = "vent"-  , ifreq    = [("medbot vent", 100)]-  , iflavour = zipPlain [BrYellow]-  , iverbHit = "menace"-  , iaspects = [ Timeout $ (2 + 1 `d` 3) * 5-               , SetFlag Periodic, SetFlag Durable ]-  , ieffects = [RefillHP 2, Explode "dense shower"]-  , idesc    = ""-  }---- * Special--bonusHP = armoredSkin-  { isymbol  = 'H'  -- '+' reserved for conditions-  , iname    = "bonus HP"-  , ifreq    = [("bonus HP", 1)]-  , iflavour = zipPlain [BrBlue]-  , iverbHit = "intimidate"-  , iweight  = 0-  , iaspects = [AddSkill SkMaxHP 1]-  , idesc    = "Special training and connections in the right places give this adventurer reinforced musculature and augmented internal organs, much more resilient to damage."-  }-braced = armoredSkin-  { isymbol  = 'B'-  , iname    = "braced"-  , ifreq    = [("braced", 1)]-  , iflavour = zipPlain [BrGreen]-  , iverbHit = "brace"-  , iweight  = 0-  , iaspects = [ AddSkill SkArmorMelee 50, AddSkill SkArmorRanged 25-               , AddSkill SkHearing 10-               , SetFlag Condition ] -- hack: display as condition-  , idesc    = "Apart of increased resilience to attacks, being braced protects from displacement by foes and other forms of forced translocation, e.g., pushing or pulling."-  }-asleep = armoredSkin-  { isymbol  = 'S'-  , iname    = "asleep"-  , ifreq    = [("asleep", 1)]-  , iflavour = zipPlain [BrGreen]  -- regenerates HP (very slowly)-  , icount   = 5-  , iverbHit = "slay"-  , iweight  = 0-  , iaspects = [AddSkill sk (-1) | sk <- [SkMove .. SkApply]]-               ++ [ AddSkill SkMelee 1, AddSkill SkAlter 1, AddSkill SkWait 1-                  , AddSkill SkSight (-3), AddSkill SkArmorMelee (-10)-                  , SetFlag Condition ]  -- hack: display as condition-  , idesc    = "Sleep helps to regain health, albeit extremely slowly. Being asleep makes you vulnerable, with gradually diminishing effects as the slumber wears off over several turns. Any non-idle action, not only combat but even yawning or stretching removes a sizable portion of the sleepiness."-  }-impressed = armoredSkin-  { isymbol  = 'I'-  , iname    = "impressed"-  , ifreq    = [("impressed", 1), ("condition", 1)]-  , iflavour = zipPlain [BrRed]-  , iverbHit = "confuse"-  , iweight  = 0-  , iaspects = [ AddSkill SkMaxCalm (-1)  -- to help player notice on HUD-                                          -- and to count as bad condition-               , SetFlag Fragile  -- to announce "no longer" only when-                                  -- all impressions gone-               , SetFlag Condition ]  -- this is really a condition,-                                      -- just not a timed condition-  , ieffects = [ OnSmash $ verbMsgLess "impressed"-               , OnSmash $ verbMsgNoLonger "impressed" ]-                   -- not periodic, so no wear each turn, so only @OnSmash@-  , idesc    = "Being impressed by one's adversary sounds like fun, but on battlefield it equals treason. Almost. Throw in depleted battle calm and it leads to mindless desertion outright."-  }---- * Allure-specific--smallBeak = fist-  { iname    = "small beak"-  , ifreq    = [("small beak", 50)]-  , icount   = 1-  , iverbHit = "nom"-  , idamage  = 2 `d` 1-  , idesc    = "Cute, but painful."-  }-razor = fist-  { iname    = "razor edge"-  , ifreq    = [("razor", 100)]-  , icount   = 1 + 1 `d` 2-  , iverbHit = "slice"-  , idamage  = 2 `d` 1-  , iaspects = [SetFlag Meleeable]  -- not Durable-  , ieffects = [ toOrganBad "weakened" (2 + 1 `dL` 3)-               , VerbNoLonger "lose all sharpness" ]-                 -- we interpret charges as sharpness of the actor or his razor'-                 -- no pronoun in the message to avoid "you lose its sharpness"-  , idesc    = ""-  }-liveWire = fist-  { iname    = "live wire"-  , ifreq    = [("live wire", 100)]-  , icount   = 1-  , iverbHit = "shock"-  , idamage  = 0-  , iaspects = [ Timeout $ 3 + 1 `d` 2-               , AddSkill SkHurtMelee 20 ]-               ++ iaspects fist-  , ieffects = [ toOrganBad "immobile" (3 + 1 `d` 3)-               , RefillHP (-1) ]-  , idesc    = ""-  }-robotBrain = armoredSkin-  { iname    = "robot brain"-  , ifreq    = [("robot brain", 100)]-  , iverbHit = "outcompute"-  , iaspects = [AddSkill sk 1 | sk <- [SkMove .. SkApply]]-               ++ [AddSkill SkMove 4]  -- can move at once when waking up-               ++ [AddSkill SkAlter 1]  -- can open doors; only easiest stairs-               ++ [AddSkill SkWait 2]  -- can brace and sleep-               -- No @SkAlter@ bonus, so can only use the easiest stairs.-               ++ [AddSkill SkApply (-1)]  -- can't even eat food, but can fling-               ++ [SetFlag Durable]-  , idesc    = ""-  }-hullPlating = armoredSkin-  { iname    = "hull plating"-  , ifreq    = [("hull plating", 100)]-  , idesc    = ""-  }-mouthVent = armoredSkin-  { iname    = "mouth vent"-  , ifreq    = [("mouth vent", 100)]-  , iflavour = zipPlain [BrMagenta]-  , iverbHit = "surprise"-  , iaspects = [ Timeout 7-               , SetFlag Periodic, SetFlag Durable ]-  , ieffects = [OneOf $ map Explode-      ["pheromone", "cruise ad hologram", "immobile mist", "smoke", "spark"]]-  , idesc    = ""-  }-geneticFlaw :: Int -> ItemKind  -- HP change varies due to body size-geneticFlaw n = armoredSkin-  { isymbol  = 'F'-  , iname    = "genetic flaw"-  , ifreq    = [ ("genetic flaw", 1)-               , (toGroupName $ "genetic flaw" <+> tshow n, 1) ]-  , iflavour = zipPlain [BrRed]-  , iverbHit = "flaw"-  , iweight  = 0-  , iaspects = [ AddSkill SkMaxHP (intToDice $ - n)-               , Odds (1 `d` 400) [AddSkill SkArmorMelee (-20)]-                                  [AddSkill SkArmorRanged (-10)]-               , SetFlag Condition ]-                   -- destroy on drop to run the @OnSmash@ effects-               ++ [AddSkill SkWait (-1) | n >= 10]-               ++ [AddSkill SkApply (-1) | n >= 10]-  , ieffects = [ OnSmash $ DropItem maxBound maxBound COrgan "condition"-                   -- key for AI is it eliminates all impression conditions-               , OnSmash $ RefillHP n-               , OnSmash $ VerbNoLonger "undergo instant infracellular decontamination" ]  -- unlike the civilian version, this one is instant and the attunement is automatic and relatively quick (the usual double cooldown when equipping items again)-  , idesc    = "Nobody is perfect. At least without infracellular engineering, which is heavily regulated, insanely expensive and automatically reverted without refund before critical medical interventions. One more reason to be a good citizen, work hard and not die often. But where is the fun in that?"-  }-geneticFlaw3 = geneticFlaw 3-geneticFlaw10 = geneticFlaw 10+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history)+-- This file is a part of the computer game Allure of the Stars+-- and is released under the terms of the GNU Affero General Public License.+-- For license and copyright information, see the file LICENSE.+--+-- | Actor organ definitions.+module Content.ItemKindOrgan+  ( -- * Group name patterns+    pattern S_FIST, pattern S_FOOT, pattern S_HOOKED_CLAW, pattern S_SMALL_CLAW, pattern S_SNOUT, pattern S_SMALL_JAW, pattern S_JAW, pattern S_LARGE_JAW, pattern S_ANTLER, pattern S_HORN, pattern S_RHINO_HORN, pattern S_TENTACLE, pattern S_TIP, pattern S_LIP, pattern S_THORN, pattern S_BOILING_FISSURE, pattern S_BEE_STING, pattern S_STING, pattern S_VENOM_TOOTH, pattern S_VENOM_FANG, pattern S_SCREECHING_BEAK, pattern S_LARGE_TAIL, pattern S_HUGE_TAIL, pattern S_ARMORED_SKIN, pattern S_BARK, pattern S_NOSTRIL, pattern S_RATLLE, pattern S_INSECT_MORTALITY, pattern S_SAPIENT_BRAIN, pattern S_ANIMAL_BRAIN, pattern S_SCENT_GLAND, pattern S_BOILING_VENT, pattern S_EYE_3, pattern S_EYE_6, pattern S_EYE_8, pattern S_VISION_6, pattern S_VISION_12, pattern S_VISION_16, pattern S_EAR_3, pattern S_EAR_6, pattern S_EAR_8, pattern S_SPEED_GLAND_5, pattern S_SPEED_GLAND_10+  , pattern SCAVENGER, pattern ALCOHOL+  , pattern S_ANIMAL_STOMACH, pattern S_HUNGRY, pattern S_RAZOR, pattern S_FLOTATION_BAG, pattern S_INK_SAC, pattern S_POWERFUL_HIND_LEGS, pattern S_COILED_TAIL, pattern S_JET_BOOSTER, pattern S_RHINO_INERTIA, pattern S_SMALL_BEAK, pattern S_LIVE_WIRE, pattern S_COOLING_VENT, pattern S_COOLING_FISSURE, pattern S_MEDBOT_VENT, pattern S_MEDBOT_FISSURE, pattern S_DUST_VENT, pattern S_DUST_FISSURE, pattern S_FUEL_VENT, pattern S_FUEL_FISSURE, pattern S_ROBOT_BRAIN, pattern S_HULL_PLATING, pattern S_MOUTH_VENT, pattern S_CRUDE_WELD+  , pattern ELECTRIC_AMBIENCE, pattern GENETIC_FLAW_3, pattern GENETIC_FLAW_10, pattern GENETIC_FLAW, pattern BACKSTORY, pattern BACKSTORY_FLUFF, pattern BACKSTORY_GOOD, pattern BACKSTORY_BAD, pattern BACKSTORY_MIXED, pattern BACKSTORY_NEUTRAL+  , organsGNSingleton, organsGN+  , -- * Content+    organs+  ) where++import Prelude ()++import Game.LambdaHack.Core.Prelude++import Content.ItemKindBlast+import Content.ItemKindTemporary+import Game.LambdaHack.Content.ItemKind+import Game.LambdaHack.Core.Dice+import Game.LambdaHack.Definition.Ability+import Game.LambdaHack.Definition.Color+import Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Definition.Flavour++-- * Group name patterns++organsGNSingleton :: [GroupName ItemKind]+organsGNSingleton =+       [S_FIST, S_FOOT, S_HOOKED_CLAW, S_SMALL_CLAW, S_SNOUT, S_SMALL_JAW, S_JAW, S_LARGE_JAW, S_ANTLER, S_HORN, S_RHINO_HORN, S_TENTACLE, S_TIP, S_LIP, S_THORN, S_BOILING_FISSURE, S_BEE_STING, S_STING, S_VENOM_TOOTH, S_VENOM_FANG, S_SCREECHING_BEAK, S_LARGE_TAIL, S_HUGE_TAIL, S_ARMORED_SKIN, S_BARK, S_NOSTRIL, S_RATLLE, S_INSECT_MORTALITY, S_SAPIENT_BRAIN, S_ANIMAL_BRAIN, S_SCENT_GLAND, S_BOILING_VENT, S_EYE_3, S_EYE_6, S_EYE_8, S_VISION_6, S_VISION_12, S_VISION_16, S_EAR_3, S_EAR_6, S_EAR_8, S_SPEED_GLAND_5, S_SPEED_GLAND_10]+    ++ [S_ANIMAL_STOMACH, S_HUNGRY, S_RAZOR, S_FLOTATION_BAG, S_INK_SAC, S_POWERFUL_HIND_LEGS, S_COILED_TAIL, S_JET_BOOSTER, S_RHINO_INERTIA, S_SMALL_BEAK, S_LIVE_WIRE, S_COOLING_VENT, S_COOLING_FISSURE, S_MEDBOT_VENT, S_MEDBOT_FISSURE, S_DUST_VENT, S_DUST_FISSURE, S_FUEL_VENT, S_FUEL_FISSURE, S_ROBOT_BRAIN, S_HULL_PLATING, S_MOUTH_VENT, S_CRUDE_WELD, BACKSTORY_FLUFF_UNKNOWN, BACKSTORY_GOOD_UNKNOWN, BACKSTORY_BAD_UNKNOWN, BACKSTORY_MIXED_UNKNOWN, BACKSTORY_NEUTRAL_UNKNOWN]++pattern S_FIST, S_FOOT, S_HOOKED_CLAW, S_SMALL_CLAW, S_SNOUT, S_SMALL_JAW, S_JAW, S_LARGE_JAW, S_ANTLER, S_HORN, S_RHINO_HORN, S_TENTACLE, S_TIP, S_LIP, S_THORN, S_BOILING_FISSURE, S_BEE_STING, S_STING, S_VENOM_TOOTH, S_VENOM_FANG, S_SCREECHING_BEAK, S_LARGE_TAIL, S_HUGE_TAIL, S_ARMORED_SKIN, S_BARK, S_NOSTRIL, S_RATLLE, S_INSECT_MORTALITY, S_SAPIENT_BRAIN, S_ANIMAL_BRAIN, S_SCENT_GLAND, S_BOILING_VENT, S_EYE_3, S_EYE_6, S_EYE_8, S_VISION_6, S_VISION_12, S_VISION_16, S_EAR_3, S_EAR_6, S_EAR_8, S_SPEED_GLAND_5, S_SPEED_GLAND_10 :: GroupName ItemKind++pattern S_ANIMAL_STOMACH, S_HUNGRY, S_RAZOR, S_FLOTATION_BAG, S_INK_SAC, S_POWERFUL_HIND_LEGS, S_COILED_TAIL, S_JET_BOOSTER, S_RHINO_INERTIA, S_SMALL_BEAK, S_LIVE_WIRE, S_COOLING_VENT, S_COOLING_FISSURE, S_MEDBOT_VENT, S_MEDBOT_FISSURE, S_DUST_VENT, S_DUST_FISSURE, S_FUEL_VENT, S_FUEL_FISSURE, S_ROBOT_BRAIN, S_HULL_PLATING, S_MOUTH_VENT, S_CRUDE_WELD, BACKSTORY_FLUFF_UNKNOWN, BACKSTORY_GOOD_UNKNOWN, BACKSTORY_BAD_UNKNOWN, BACKSTORY_MIXED_UNKNOWN, BACKSTORY_NEUTRAL_UNKNOWN :: GroupName ItemKind++organsGN :: [GroupName ItemKind]+organsGN =+     SCAVENGER : ALCOHOL+     : [ELECTRIC_AMBIENCE, GENETIC_FLAW_3, GENETIC_FLAW_10, GENETIC_FLAW, BACKSTORY, BACKSTORY_FLUFF, BACKSTORY_GOOD, BACKSTORY_BAD, BACKSTORY_MIXED, BACKSTORY_NEUTRAL]++pattern SCAVENGER :: GroupName ItemKind+pattern ALCOHOL :: GroupName ItemKind++pattern ELECTRIC_AMBIENCE, GENETIC_FLAW_3, GENETIC_FLAW_10, GENETIC_FLAW, BACKSTORY, BACKSTORY_FLUFF, BACKSTORY_GOOD, BACKSTORY_BAD, BACKSTORY_MIXED, BACKSTORY_NEUTRAL :: GroupName ItemKind++pattern S_FIST = GroupName "fist"+pattern S_FOOT = GroupName "foot"+pattern S_HOOKED_CLAW = GroupName "hooked claw"+pattern S_SMALL_CLAW = GroupName "small claw"+pattern S_SNOUT = GroupName "snout"+pattern S_SMALL_JAW = GroupName "small jaw"+pattern S_JAW = GroupName "jaw"+pattern S_LARGE_JAW = GroupName "large jaw"+pattern S_ANTLER = GroupName "antler"+pattern S_HORN = GroupName "horn"+pattern S_RHINO_HORN = GroupName "rhino horn"+pattern S_TENTACLE = GroupName "tentacle"+pattern S_TIP = GroupName "tip"+pattern S_LIP = GroupName "lip"+pattern S_THORN = GroupName "thorn"+pattern S_BOILING_FISSURE = GroupName "boiling fissure"+pattern S_BEE_STING = GroupName "bee sting"+pattern S_STING = GroupName "sting"+pattern S_VENOM_TOOTH = GroupName "venom tooth"+pattern S_VENOM_FANG = GroupName "venom fang"+pattern S_SCREECHING_BEAK = GroupName "screeching beak"+pattern S_LARGE_TAIL = GroupName "large tail"+pattern S_HUGE_TAIL = GroupName "huge tail"+pattern S_ARMORED_SKIN = GroupName "armored skin"+pattern S_BARK = GroupName "bark"+pattern S_NOSTRIL = GroupName "nostril"+pattern S_RATLLE = GroupName "rattle"+pattern S_INSECT_MORTALITY = GroupName "insect mortality"+pattern S_SAPIENT_BRAIN = GroupName "sapient brain"+pattern S_ANIMAL_BRAIN = GroupName "animal brain"+pattern S_SCENT_GLAND = GroupName "scent gland"+pattern S_BOILING_VENT = GroupName "boiling vent"+pattern S_EYE_3 = GroupName "eye 3"+pattern S_EYE_6 = GroupName "eye 6"+pattern S_EYE_8 = GroupName "eye 8"+pattern S_VISION_6 = GroupName "vision 6"+pattern S_VISION_12 = GroupName "vision 12"+pattern S_VISION_16 = GroupName "vision 16"+pattern S_EAR_3 = GroupName "ear 3"+pattern S_EAR_6 = GroupName "ear 6"+pattern S_EAR_8 = GroupName "ear 8"+pattern S_SPEED_GLAND_5 = GroupName "speed gland 5"+pattern S_SPEED_GLAND_10 = GroupName "speed gland 10"++pattern SCAVENGER = GroupName "scavenger"+pattern ALCOHOL = GroupName "alcohol"++-- ** Allure-specific+pattern S_ANIMAL_STOMACH = GroupName "animal stomach"+pattern S_HUNGRY = GroupName "hungry"+pattern S_RAZOR = GroupName "razor"+pattern S_FLOTATION_BAG = GroupName "flotation bag"+pattern S_INK_SAC = GroupName "inc sac"+pattern S_POWERFUL_HIND_LEGS = GroupName "powerful hind legs"+pattern S_COILED_TAIL = GroupName "coiled tail"+pattern S_JET_BOOSTER = GroupName "jet booster"+pattern S_RHINO_INERTIA = GroupName "rhino inertia"+pattern S_SMALL_BEAK = GroupName "small beak"+pattern S_LIVE_WIRE = GroupName "live wire"+pattern S_COOLING_VENT = GroupName "cooling vent"+pattern S_COOLING_FISSURE = GroupName "cooling fissure"+pattern S_MEDBOT_VENT = GroupName "medbot vent"+pattern S_MEDBOT_FISSURE = GroupName "medbot fissure"+pattern S_DUST_VENT = GroupName "dust vent"+pattern S_DUST_FISSURE = GroupName "dust fissure"+pattern S_FUEL_VENT = GroupName "fuel vent"+pattern S_FUEL_FISSURE = GroupName "fuel fissure"+pattern S_ROBOT_BRAIN = GroupName "robot brain"+pattern S_HULL_PLATING = GroupName "hull plating"+pattern S_MOUTH_VENT = GroupName "mouth vent"+pattern S_CRUDE_WELD = GroupName "crude weld"++pattern ELECTRIC_AMBIENCE = GroupName "electric ambience"+pattern GENETIC_FLAW_3 = GroupName "genetic flaw 3"+pattern GENETIC_FLAW_10 = GroupName "genetic flaw 10"+pattern GENETIC_FLAW = GroupName "genetic flaw"+pattern BACKSTORY = GroupName "backstory"+pattern BACKSTORY_FLUFF_UNKNOWN = GroupName "fluff backstory unknown"+pattern BACKSTORY_FLUFF = GroupName "fluff backstory"+pattern BACKSTORY_GOOD_UNKNOWN = GroupName "good backstory unknown"+pattern BACKSTORY_GOOD = GroupName "good backstory"+pattern BACKSTORY_BAD_UNKNOWN = GroupName "bad backstory unknown"+pattern BACKSTORY_BAD = GroupName "bad backstory"+pattern BACKSTORY_MIXED_UNKNOWN = GroupName "mixed backstory unknown"+pattern BACKSTORY_MIXED = GroupName "mixed backstory"+pattern BACKSTORY_NEUTRAL_UNKNOWN = GroupName "neutral backstory unk."+pattern BACKSTORY_NEUTRAL = GroupName "neutral backstory"++-- * Content++organs :: [ItemKind]+organs =+  [fist, foot, hookedClaw, smallClaw, snout, smallJaw, jaw, largeJaw, antler, horn, rhinoHorn, tentacle, tip, lip, thorn, boilingFissure, arsenicFissure, sulfurFissure, beeSting, sting, venomTooth, venomFang, screechingBeak, largeTail, hugeTail, armoredSkin, bark, eye3, eye6, eye8, vision6, vision12, vision16, nostril, ear3, ear6, ear8, rattleOrgan, insectMortality, sapientBrain, animalBrain, speedGland5, speedGland10, scentGland, boilingVent, arsenicVent, sulfurVent, bonusHP, braced, asleep, impressed]+  -- Allure-specific+  ++ [animalStomach, hungry, smallBeak, razor, liveWire, flotationBag, inkSac, powerfulHindLegs, coiledTail, jetBooster, rhinoInertia, electricAmbience, electricAmbienceRecharge, robotBrain, hullPlating, mouthVent, dustVent, dustFissure, fuelVent, fuelFissure, geneticFlaw3BadArmorMelee, geneticFlaw3BadArmorRanged, geneticFlaw10BadArmorMelee, geneticFlaw10BadArmorRanged, backstoryFluffTemplate, backstoryFluff1, backstoryGoodTemplate, backstoryGood1, backstoryGood2, backstoryGood3, backstoryBadTemplate, backstoryBad1, backstoryBad2, backstoryBad3, backstoryBad4, backstoryMixedTemplate, backstoryMixed1, backstoryMixed2, backstoryNeutralTemplate, backstoryNeutral1, backstoryNeutral2, backstoryNeutral3]++fist,    foot, hookedClaw, smallClaw, snout, smallJaw, jaw, largeJaw, antler, horn, rhinoHorn, tentacle, tip, lip, thorn, boilingFissure, arsenicFissure, sulfurFissure, beeSting, sting, venomTooth, venomFang, screechingBeak, largeTail, hugeTail, armoredSkin, bark, eye3, eye6, eye8, vision6, vision12, vision16, nostril, ear3, ear6, ear8, rattleOrgan, insectMortality, sapientBrain, animalBrain, speedGland5, speedGland10, scentGland, boilingVent, arsenicVent, sulfurVent, bonusHP, braced, asleep, impressed :: ItemKind+-- Allure-specific+animalStomach,       hungry, smallBeak, razor, liveWire, flotationBag, inkSac, powerfulHindLegs, coiledTail, jetBooster, rhinoInertia, electricAmbience, electricAmbienceRecharge, robotBrain, hullPlating, mouthVent, dustVent, dustFissure, fuelVent, fuelFissure, geneticFlaw3BadArmorMelee, geneticFlaw3BadArmorRanged, geneticFlaw10BadArmorMelee, geneticFlaw10BadArmorRanged, backstoryFluffTemplate, backstoryFluff1, backstoryGoodTemplate, backstoryGood1, backstoryGood2, backstoryGood3, backstoryBadTemplate, backstoryBad1, backstoryBad2, backstoryBad3, backstoryBad4, backstoryMixedTemplate, backstoryMixed1, backstoryMixed2, backstoryNeutralTemplate, backstoryNeutral1, backstoryNeutral2, backstoryNeutral3 :: ItemKind++-- * No-cooldown melee damage organs without effects++thorn = fist+  { isymbol  = '-'+  , iname    = "thorn"+  , ifreq    = [(S_THORN, 1)]+  , icount   = 2 + 1 `d` 2  -- unrealistic, but not boring+  , iverbHit = "puncture"+  , idamage  = 1 `d` 1+  , iaspects = [SetFlag Meleeable]  -- not Durable+  , ieffects = [VerbNoLonger "be not so thorny any more" "."]+  , idesc    = "Sharp yet brittle."+  }+tip = fist+  { iname    = "tip"+  , ifreq    = [(S_TIP, 1)]+  , icount   = 1+  , iverbHit = "poke"+  , idamage  = 1 `d` 1+  , idesc    = ""+  }+fist = ItemKind+  { isymbol  = ','+  , iname    = "fist"+  , ifreq    = [(S_FIST, 1)]+  , iflavour = zipPlain [Red]+  , icount   = 2+  , irarity  = [(1, 1)]+  , iverbHit = "punch"+  , iweight  = 2000+  , idamage  = 2 `d` 1+  , iaspects = [SetFlag Durable, SetFlag Meleeable]+  , ieffects = []+  , idesc    = "Simple but effective."+  , ikit     = []+  }+foot = fist+  { iname    = "foot"+  , ifreq    = [(S_FOOT, 1)]+  , iverbHit = "kick"+  , idamage  = 2 `d` 1+  , idesc    = "A weapon you can still use if disarmed."+                 -- great example of tutorial hints inside a flavourful text+  }+smallClaw = fist+  { iname    = "small claw"+  , ifreq    = [(S_SMALL_CLAW, 1)]+  , iverbHit = "slash"+  , idamage  = 2 `d` 1+  , idesc    = "A pearly spike."+  }+snout = fist+  { iname    = "snout"+  , ifreq    = [(S_SNOUT, 1)]+  , icount   = 1+  , iverbHit = "nudge"+  , idamage  = 2 `d` 1+  , idesc    = "Sensitive and wide-nostrilled."+  }+smallJaw = fist+  { iname    = "small jaw"+  , ifreq    = [(S_SMALL_JAW, 1)]+  , icount   = 1+  , iverbHit = "rip"+  , idamage  = 3 `d` 1+  , idesc    = "Filled with small, even teeth."+  }++-- * Cooldown melee damage organs without effects++tentacle = fist  -- two copies only+  { iname    = "tentacle"+  , ifreq    = [(S_TENTACLE, 1)]+  , iverbHit = "slap"+  , idamage  = 4 `d` 1+  , iaspects = Timeout 3  -- minimal timeout that lets other organs show+               : iaspects fist+  , idesc    = "Damp and dextrous."+  }+jaw = fist+  { iname    = "jaw"+  , ifreq    = [(S_JAW, 1)]+  , icount   = 1+  , iverbHit = "rip"+  , idamage  = 5 `d` 1+  , iaspects = Timeout (2 + 1 `d` 2)  -- no effect, but limit raw damage+               : iaspects fist+  , idesc    = "Delivers a powerful bite."+  }+horn = fist+  { iname    = "horn"+  , ifreq    = [(S_HORN, 1)]+  , iverbHit = "impale"+  , idamage  = 5 `d` 1+  , iaspects = [ Timeout 7  -- no effect, but limit raw damage; two copies+               , AddSkill SkArmorMelee 10 ]  -- bonus doubled+               ++ iaspects fist+  , idesc    = "Sharp and long, for defence or attack."+  }+largeTail = fist+  { iname    = "large tail"+  , ifreq    = [(S_LARGE_TAIL, 1)]+  , icount   = 1+  , iverbHit = "knock"+  , idamage  = 6 `d` 1  -- not sharp+  , iaspects = Timeout (4 + 1 `d` 2)  -- one copy, so can be low+               : iaspects fist+  , idesc    = "Almost as long as the trunk."+  }+largeJaw = fist  -- organs can't be too weak, because some non-humans also use+                 -- human weapons and then the sudden contrast would be cruel+  { iname    = "large jaw"+  , ifreq    = [(S_LARGE_JAW, 1)]+  , icount   = 1+  , iverbHit = "crush"+  , idamage  = 10 `d` 1  -- tops the best non-crafted, non-unique human weapons+  , iaspects = Timeout (8 + 1 `d` 2)  -- no effect, but limit raw damage+               : iaspects fist+  , idesc    = "Enough to swallow anything in a single gulp."+  }++-- * Direct damage organs with effects++beeSting = fist+  { isymbol  = '-'+  , iname    = "bee sting"+  , ifreq    = [(S_BEE_STING, 1)]+  , icount   = 1+  , iverbHit = "sting"+  , idamage  = 0+  , iaspects = [ AddSkill SkArmorMelee 200, AddSkill SkArmorRanged 45+               , SetFlag Meleeable ]  -- not Durable+  , ieffects = [Paralyze 10, RefillHP 3]  -- low gain; no tragedy if dies early+                 -- no special message when runs out, because it's 1 copy+  , idesc    = "Painful, but beneficial."+  }+sting = fist+  { isymbol  = '-'+  , iname    = "sting"+  , ifreq    = [(S_STING, 1)]+  , icount   = 1+  , iverbHit = "inject"+  , idamage  = 1 `d` 1+  , iaspects = Timeout (10 - 1 `dL` 4)+               : iaspects fist+  , ieffects = [toOrganBad S_RETAINING (3 + 1 `d` 3)]+  , idesc    = "Painful, debilitating and harmful."+  }+lip = fist+  { iname    = "lip"+  , ifreq    = [(S_LIP, 1)]+  , icount   = 1+  , iverbHit = "lap"+  , idamage  = 1 `d` 1+  , iaspects = Timeout (3 + 1 `d` 2)+               : iaspects fist+  , ieffects = [toOrganBad S_WEAKENED (1 + 1 `dL` 3)]+  , idesc    = ""+  }+venomTooth = fist+  { isymbol  = '-'+  , iname    = "venom tooth"+  , ifreq    = [(S_VENOM_TOOTH, 1)]+  , iverbHit = "bite"+  , idamage  = 1 `d` 1+  , iaspects = Timeout (10 - 1 `dL` 3)+               : iaspects fist+  , ieffects = [toOrganBad S_SLOWED (2 + 1 `dL` 3)]+  , idesc    = "A chilling numbness spreads from its bite."+  }+hookedClaw = fist+  { isymbol  = '-'+  , iname    = "hooked claw"+  , ifreq    = [(S_HOOKED_CLAW, 1)]+  , icount   = 2  -- even if more, only the fore claws used for fighting+  , iverbHit = "hook"+  , idamage  = 2 `d` 1+  , iaspects = Timeout (12 - 1 `dL` 3)+               : iaspects fist+  , ieffects = [toOrganBad S_SLOWED 2]+  , idesc    = "A curved talon."+  }+screechingBeak = fist+  { isymbol  = '-'+  , iname    = "screeching beak"+  , ifreq    = [(S_SCREECHING_BEAK, 1)]+  , icount   = 1+  , iverbHit = "peck"+  , idamage  = 3 `d` 1+  , iaspects = Timeout (7 - 1 `dL` 3)+               : iaspects fist+  , ieffects = [Summon SCAVENGER $ 1 `dL` 3]+  , idesc    = "Both a weapon and a beacon, calling more scavengers to the meal."+  }+antler = fist+  { isymbol  = '-'+  , iname    = "antler"+  , ifreq    = [(S_ANTLER, 1)]+  , iverbHit = "ram"+  , idamage  = 4 `d` 1+  , iaspects = [ Timeout $ 3 + (1 `d` 3) * 3+               , AddSkill SkArmorMelee 10 ]  -- bonus doubled+               ++ iaspects fist+  , ieffects = [PushActor (ThrowMod 100 50 1)]  -- 1 step, slow+  , idesc    = ""+  }+rhinoHorn = fist+  { isymbol  = '-'+  , iname    = "ugly horn"  -- made of keratin, unlike real horns+  , ifreq    = [(S_RHINO_HORN, 1)]+  , icount   = 1  -- single, unlike real horns+  , iverbHit = "gore"+  , idamage  = 4 `d` 1+  , iaspects = Timeout 5+               : iaspects fist+  , ieffects = [Impress, Yell]  -- the owner is a mid-boss, after all+  , idesc    = "Very solid, considering it has the same composition as fingernails."+  }+hugeTail = fist+  { isymbol  = '-'+  , iname    = "huge tail"+  , ifreq    = [(S_HUGE_TAIL, 1)]+  , icount   = 1+  , iverbHit = "upend"+  , idamage  = 7 `d` 1+  , iaspects = Timeout (7 + 1 `d` 2)+               : iaspects fist+                 -- timeout higher, lest they regain push before closing again+  , ieffects = [PushActor (ThrowMod 200 50 1)]  -- 1 step, fast+      -- 2 steps would be too hard to counteract via second line of heroes+  , idesc    = "This immense and muscular tail transfers a huge momentum in a matter of seconds."+  }++-- * Melee weapons without direct damage++venomFang = fist+  { isymbol  = '-'+  , iname    = "venom fang"+  , ifreq    = [(S_VENOM_FANG, 1)]+  , iverbHit = "bite"+  , idamage  = 0+  , iaspects = Timeout (10 - 1 `dL` 5)+               : iaspects fist+  , ieffects = [toOrganNoTimer S_POISONED]+  , idesc    = "Dripping with deadly venom."+  }++-- * Special melee weapons++sulfurFissure = boilingFissure+  { iname    = "fissure"+  , ifreq    = [(S_MEDBOT_FISSURE, 1)]+  , icount   = 2 + 1 `d` 2+  , idamage  = 0  -- heal not via (negative) idamage, for armour would block it+  , iaspects = SetFlag Benign : iaspects boilingFissure+  , ieffects = [ RefillHP 5+               , toOrganNoTimer S_HUNGRY  -- the metabolic price to pay+               , VerbNoLonger "run out of nano medbot liquid" "." ]+  , idesc    = ""+  }+boilingFissure = fist+  { isymbol  = '-'+  , iname    = "fissure"+  , ifreq    = [(S_BOILING_FISSURE, 1)]+  , icount   = 2 + 1 `d` 2+  , iverbHit = "hiss at"+  , idamage  = 2 `d` 1  -- can remove hunger, so high price+  , iaspects = [ AddSkill SkHurtMelee 20  -- decreasing as count decreases+               , SetFlag Meleeable ]  -- not Durable+  , ieffects = [ DropItem 1 1 COrgan CONDITION  -- useful; limited; HP price+               , VerbNoLonger "widen the crack, releasing pressure" "." ]+  , idesc    = ""+  }+arsenicFissure = boilingFissure+  { iname    = "fissure"+  , ifreq    = [(S_COOLING_FISSURE, 1)]+  , icount   = 3 + 1 `d` 3+  , idamage  = 2 `d` 1+  , ieffects = [ toOrganBad S_PARSIMONIOUS (5 + 1 `d` 3)+                   -- weaken/freeze, impacting intellectual abilities first+               , VerbNoLonger "clog with ice" "." ]+  , idesc    = ""+  }++-- * Armor organs++armoredSkin = ItemKind+  { isymbol  = ','+  , iname    = "armored skin"+  , ifreq    = [(S_ARMORED_SKIN, 1)]+  , iflavour = zipPlain [Red]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "bash"+  , iweight  = 2000+  , idamage  = 0+  , iaspects = [ AddSkill SkArmorMelee 30, AddSkill SkArmorRanged 15+               , SetFlag Durable ]+  , ieffects = []+  , idesc    = "Homemade armour is just as good."  -- hmm, it may get confused with leather armor jackets, etc.+  , ikit     = []+  }+bark = armoredSkin+  { iname    = "bark"+  , ifreq    = [(S_BARK, 1)]+  , idesc    = ""+  }++-- * Sense organs++eye :: Int -> GroupName ItemKind -> ItemKind+eye n grp = armoredSkin+  { iname    = "eye"+  , ifreq    = [(grp, 1)]+  , icount   = 2+  , iverbHit = "glare at"+  , iaspects = [ AddSkill SkSight (intToDice n)+               , SetFlag Durable ]+  , idesc    = "A piercing stare."+  }+eye3 = eye 3 S_EYE_3+eye6 = eye 6 S_EYE_6+eye8 = eye 8 S_EYE_8+vision :: Int -> GroupName ItemKind -> ItemKind+vision n grp = armoredSkin+  { iname    = "vision"+  , ifreq    = [(grp, 1)]+  , iverbHit = "visualize"+  , iaspects = [ AddSkill SkSight (intToDice n)+               , SetFlag Durable ]+  , idesc    = ""+  }+vision6 = vision 6 S_VISION_6+vision12 = vision 12 S_VISION_12+vision16 = vision 16 S_VISION_16+nostril = armoredSkin+  { iname    = "nostril"+  , ifreq    = [(S_NOSTRIL, 1)]+  , icount   = 2+  , iverbHit = "snuff"+  , iaspects = [ AddSkill SkSmell 1  -- times 2, from icount+               , SetFlag Durable ]+  , idesc    = ""+  }+ear :: Int -> GroupName ItemKind -> ItemKind+ear n grp = armoredSkin+  { iname    = "ear"+  , ifreq    = [(grp, 1)]+  , icount   = 2+  , iverbHit = "overhear"+  , iaspects = [ AddSkill SkHearing (intToDice n)+               , SetFlag Durable ]+  , idesc    = ""+  }+ear3 = ear 3 S_EAR_3+ear6 = ear 6 S_EAR_6+ear8 = ear 8 S_EAR_8++-- * Assorted++rattleOrgan = armoredSkin+  { iname    = "rattle"+  , ifreq    = [(S_RATLLE, 1)]+  , iverbHit = "announce"+  , iaspects = [ Timeout $ 10 + (1 `d` 3) * 10  -- long, to limit spam+               , SetFlag Periodic, SetFlag Durable ]+  , ieffects = [Yell, RefillCalm 5]+  , idesc    = ""+  }+insectMortality = armoredSkin+  { iname    = "insect mortality"+  , ifreq    = [(S_INSECT_MORTALITY, 1)]+  , iverbHit = "age"+  , iaspects = [ AddSkill SkAggression 2  -- try to attack before you die+               , Timeout $ 30 + (1 `d` 3) * 10  -- die very slowly+               , SetFlag Periodic, SetFlag Durable ]+  , ieffects = [RefillHP (-1), Yell]+  , idesc    = ""+  }+sapientBrain = armoredSkin+  { iname    = "sapient brain"+  , ifreq    = [(S_SAPIENT_BRAIN, 1)]+  , iverbHit = "outbrain"+  , iaspects = [AddSkill sk 1 | sk <- [SkMove .. SkApply]]+               ++ [AddSkill SkMove 4]  -- can move at once when waking up+               ++ [AddSkill SkAlter 4]  -- can use all stairs; dig rubble, ice+               ++ [AddSkill SkWait 2]  -- can brace and sleep+               ++ [AddSkill SkApply 1]  -- can use most items, not just foods+               ++ [SetFlag Durable]+  , idesc    = ""+  }+animalBrain = armoredSkin+  { iname    = "animal brain"+  , ifreq    = [(S_ANIMAL_BRAIN, 1)]+  , iverbHit = "blank"+  , iaspects = [AddSkill sk 1 | sk <- [SkMove .. SkApply]]+               ++ [AddSkill SkMove 4]  -- can move at once when waking up+               ++ [AddSkill SkAlter 2]  -- can use normal stairs; can't dig+               ++ [AddSkill SkWait 2]  -- can brace and sleep+               -- No @SkApply@ bonus, so can only apply foods. Note, however,+               -- that AI doesn't risk applying unIded items, so in early+               -- game animals won't eat anything.+               ++ [AddSkill SkDisplace (-1)]  -- no melee tactics+               ++ [AddSkill SkMoveItem (-1)]  -- no item gathering+               ++ [AddSkill SkProject (-1)]  -- nor item flinging+               ++ [SetFlag Durable]+  , idesc    = ""+  }+speedGland :: Int -> GroupName ItemKind -> ItemKind+speedGland n grp = armoredSkin+  { isymbol  = '-'+  , iname    = "speed gland"+  , ifreq    = [(grp, 1)]+  , iverbHit = "spit at"+  , iaspects = [ Timeout $ intToDice (100 `div` n)+               , AddSkill SkSpeed $ intToDice n+               , AddSkill SkArmorMelee $ intToDice $ - n * 5+               , SetFlag Periodic, SetFlag Durable ]+  , ieffects = [RefillHP 1]+  , idesc    = ""+  }+speedGland5 = speedGland 5 S_SPEED_GLAND_5+speedGland10 = speedGland 10 S_SPEED_GLAND_10+scentGland = armoredSkin+  { isymbol  = '-'+  , iname    = "scent gland"+  , ifreq    = [(S_SCENT_GLAND, 1)]+  , icount   = 10 + 1 `d` 3  -- runs out+  , iverbHit = "spray at"+  , iaspects = [ Timeout $ (1 `d` 3) * 10+               , SetFlag Periodic, SetFlag Fragile ]  -- not Durable+  , ieffects = [ VerbNoLonger "look spent" "."+               , ApplyPerfume+               , Explode S_DISTRESSING_ODOR ]+                   -- keep explosion at the end to avoid the ambiguity of+                   -- "of ([foo explosion] of [bar])"+  , idesc    = ""+  }+sulfurVent = armoredSkin+  { isymbol  = 'v'+  , iname    = "vent"+  , ifreq    = [(S_MEDBOT_VENT, 1)]+  , iflavour = zipPlain [BrYellow]+  , iverbHit = "menace"+  , iaspects = [ Timeout $ (3 + 1 `d` 3) * 5+               , SetFlag Periodic, SetFlag Durable ]+  , ieffects = [RefillHP 2, Explode S_MELEE_PROTECTIVE_BALM]+  , idesc    = ""+  }+boilingVent = armoredSkin+  { isymbol  = 'v'+  , iname    = "vent"+  , ifreq    = [(S_BOILING_VENT, 1)]+  , iflavour = zipPlain [BrGreen]+  , iverbHit = "menace"+  , iaspects = [ Timeout $ (4 + 1 `d` 3) * 5+               , SetFlag Periodic, SetFlag Durable ]+  , ieffects = [RefillHP 2, Explode S_BOILING_WATER]+  , idesc    = ""+  }+arsenicVent = armoredSkin+  { isymbol  = 'v'+  , iname    = "vent"+  , ifreq    = [(S_COOLING_VENT, 1)]+  , iflavour = zipPlain [White]+  , iverbHit = "menace"+  , iaspects = [ Timeout $ (2 + 1 `d` 3) * 5+               , SetFlag Periodic, SetFlag Durable ]+  , ieffects = [RefillHP 2, Explode S_VIOLENT_SLOWNESS_MIST]+  , idesc    = ""+  }++-- * Special++bonusHP = armoredSkin+  { isymbol  = 'H'  -- '+' reserved for conditions+  , iname    = "bonus HP"+  , ifreq    = [(S_BONUS_HP, 1)]+  , iflavour = zipPlain [BrBlue]+  , iverbHit = "intimidate"+  , iweight  = 0+  , iaspects = [AddSkill SkMaxHP 1]+  , idesc    = "Special training and connections in the right places give this adventurer reinforced musculature and augmented internal organs, much more resilient to damage."+  }+braced = armoredSkin+  { isymbol  = 'B'+  , iname    = "braced"+  , ifreq    = [(S_BRACED, 1)]+  , iflavour = zipPlain [BrGreen]+  , iverbHit = "brace"+  , iweight  = 0+  , iaspects = [ AddSkill SkArmorMelee 50, AddSkill SkArmorRanged 25+               , AddSkill SkHearing 10+               , SetFlag Condition ] -- hack: display as condition+  , idesc    = "Apart of increased resilience to attacks, being braced protects from displacement by foes and other forms of forced translocation, e.g., pushing or pulling."+  }+asleep = armoredSkin+  { isymbol  = 'S'+  , iname    = "asleep"+  , ifreq    = [(S_ASLEEP, 1)]+  , iflavour = zipPlain [BrGreen]  -- regenerates HP (very slowly)+  , icount   = 5+  , iverbHit = "slay"+  , iweight  = 0+  , iaspects = [AddSkill sk (-1) | sk <- [SkMove .. SkApply]]+               ++ [ AddSkill SkMelee 1, AddSkill SkAlter 1, AddSkill SkWait 1+                  , AddSkill SkSight (-3), AddSkill SkArmorMelee (-10)+                  , SetFlag Condition ]  -- hack: display as condition+  , idesc    = "Sleep helps to regain health, albeit extremely slowly. Being asleep makes you vulnerable, with gradually diminishing effects as the slumber wears off over several turns. Any non-idle action, not only combat but even yawning or stretching removes a sizable portion of the sleepiness."+  }+impressed = armoredSkin+  { isymbol  = 'I'+  , iname    = "impressed"  -- keep the same as in @ifreq@, to simplify code+  , ifreq    = [(S_IMPRESSED, 1), (CONDITION, 1)]+  , iflavour = zipPlain [BrRed]+  , iverbHit = "confuse"+  , iweight  = 0+  , iaspects = [ AddSkill SkMaxCalm (-1)  -- to help player notice on HUD+                                          -- and to count as bad condition+               , AddSkill SkArmorMelee (-3)+               , SetFlag Fragile  -- to announce "no longer" only when+                                  -- all copies gone+               , SetFlag Condition ]  -- this is really a condition,+                                      -- just not a timed condition+  , ieffects = [ OnSmash $ verbMsgLess "impressed"+               , OnSmash $ verbMsgNoLonger "impressed" ]+                   -- not periodic, so no wear each turn, so only @OnSmash@+  , idesc    = "Being impressed by one's adversary sounds like fun, but on battlefield it equals treason. Almost. Throw in depleted battle calm and it leads to mindless desertion outright."+  }++-- * Allure-specific melee weapons++smallBeak = fist+  { iname    = "small beak"+  , ifreq    = [(S_SMALL_BEAK, 1)]+  , icount   = 1+  , iverbHit = "nom"+  , idamage  = 2 `d` 1+  , idesc    = "Cute, but painful."+  }+liveWire = fist+  { isymbol  = '-'+  , iname    = "live wire"+  , ifreq    = [(S_LIVE_WIRE, 1)]+  , icount   = 1+  , iverbHit = "shock"+  , idamage  = 0+  , iaspects = Timeout (2 + 1 `d` 2)+               : iaspects fist+  , ieffects = [ Discharge 1 $ 80 - 1 `d` 40+               , RefillHP (-1) ]+  , idesc    = ""+  }+razor = fist+  { isymbol  = '-'+  , iname    = "razor edge"+  , ifreq    = [(S_RAZOR, 1)]+  , icount   = 1 + 1 `d` 2+  , iverbHit = "slice"+  , idamage  = 2 `d` 1+  , iaspects = [ SetFlag Meleeable  -- not Durable+               , Timeout 4 ]  -- but cooldown to use other weapons+  , ieffects = [ toOrganBad S_WEAKENED (2 + 1 `dL` 3)+               , VerbNoLonger "lose all sharpness" "." ]+                 -- we interpret charges as sharpness of the actor or his razor'+                 -- no pronoun in the message to avoid "you lose its sharpness"+  , idesc    = ""+  }+dustFissure = boilingFissure+  { iname    = "fissure"+  , ifreq    = [(S_DUST_FISSURE, 1)]+  , icount   = 3 + 1 `d` 3+  , idamage  = 1 `d` 1+  , ieffects = [ toOrganBad S_WEAKENED 20+               , VerbNoLonger "cough one last time" "." ]+  , idesc    = ""+  }+fuelFissure = boilingFissure+  { iname    = "fissure"+  , ifreq    = [(S_FUEL_FISSURE, 1)]+  , icount   = 3 + 1 `d` 3+  , idamage  = 0+  , ieffects = [ Burn 1+               , VerbNoLonger "have its fissures mended by emergency auto-sealants" "." ]+  , idesc    = ""+  }++-- * Allure-specific other++animalStomach = armoredSkin+  { isymbol  = 'u'+  , iname    = "animal stomach"+  , ifreq    = [(S_ANIMAL_STOMACH, 1)]+  , iverbHit = "burp"+  , iaspects = [ Timeout $ 500 + (1 `d` 3) * 50  -- hunger very slowly+               , SetFlag Periodic, SetFlag Durable ]+  , ieffects = [toOrganNoTimer S_HUNGRY]+  , idesc    = ""+  }+hungry = armoredSkin+  { isymbol  = 'U'+  , iname    = "hungry"  -- keep the same as in @ifreq@, to simplify code+  , ifreq    = [(S_HUNGRY, 1), (CONDITION, 1)]+  , iflavour = zipPlain [BrRed]+  , icount   = 1+  , iverbHit = "pang"+  , iweight  = 0+  , iaspects = [ AddSkill SkMaxHP (-1)+               , SetFlag Fragile  -- to announce "no longer" only when+                                  -- all copies gone+               , SetFlag Condition ]  -- this is really a condition,+                                      -- just not a timed condition+  , ieffects = [ OnSmash $ verbMsgLess "hungry"+               , OnSmash $ verbMsgNoLonger "hungry" ]+                   -- not periodic, so no wear each turn, so only @OnSmash@+  , idesc    = "Hunger limits physical fitness. In extreme cases, when compounded, it causes such fragility that the slightest stress becomes lethal."+  }+flotationBag = armoredSkin+  { isymbol  = 'O'+  , iname    = "flotation bag"+  , ifreq    = [(S_FLOTATION_BAG, 1)]+  , iverbHit = "uplift"+  , iaspects = [AddSkill SkArmorRanged (-15), SetFlag Durable]+  , ieffects = [OnSmash $ Explode S_FOCUSED_CONCUSSION]+                  -- if too weak, use S_FOCUSED_FRAGMENTATION that is+                  -- less thematic than CONCUSSION, but more likely to cause+                  -- the chain reaction among peers that we are after+  , idesc    = "A large organ that enables effortless flight. It is essentially a hydrogen container with easily regulated internal pressure. It evolved a protection against blunt trauma, but not against puncture."+  }+inkSac = armoredSkin  -- neither melee nor aspects nor periodic, so to be+  { isymbol  = '"'    -- triggered, needs a special symbol+  , iname    = "ink sac"+  , ifreq    = [(S_INK_SAC, 1)]+  , iverbHit = "squirt"+  , iaspects = [Timeout $ 3 + 1 `d` 3, SetFlag Durable]+  , ieffects = [ Explode S_SMOKE  -- weak, but lingers long+               , PullActor (ThrowMod 200 50 1)  -- 1 step, fast+               , RefillCalm 100 ]  -- balance @Explode@ so that AI uses it+  , idesc    = ""  -- TODO: https://en.wikipedia.org/wiki/Octopus#Ink_sac+  }+powerfulHindLegs = armoredSkin  -- neither melee nor periodic so to trigger+  { isymbol  = '"'              -- needs a special symbol+  , iname    = "pair"+  , ifreq    = [(S_POWERFUL_HIND_LEGS, 1)]+  , iverbHit = "jump"+  , iaspects = [ ELabel "of powerful hind legs"+               , Timeout $ 3 + 1 `d` 3, SetFlag Durable ]+  , ieffects = [toOrganGood S_HASTED 1]  -- see comments about jumping pole+  , idesc    = "With legs like that, just a moment of preparation is enough to make a rapid succession of huge leaps."  -- no damage, but feet or claws are separate organs and they can be weapons+  }+coiledTail = powerfulHindLegs+  { iname    = "coiled tail"+  , ifreq    = [(S_COILED_TAIL, 1)]+  , iverbHit = "spring"+  , iaspects = [Timeout $ 3 + 1 `d` 3, SetFlag Durable]+  , idesc    = "When the coiled tail springs, expect a lurch that leaves you no time to react."+  }+jetBooster = armoredSkin  -- neither melee nor periodic so to be triggered,+  { isymbol  = '"'        -- needs a special symbol+  , iname    = "jet booster"+  , ifreq    = [(S_JET_BOOSTER, 1)]+  , iverbHit = "dart"+  , iaspects = [Timeout $ 4 + 1 `d` 2, SetFlag Durable]+  , ieffects = [ PushActor (ThrowMod 800 100 1)  -- 8 steps, 2 turns+               , RefillCalm 100 ]  -- give AI an incentive to use it+  , idesc    = "The throttles are quite erratic with age, but the punch is none the less."+  }+rhinoInertia = jetBooster+  { iname    = "rhino inertia"+  , ifreq    = [(S_RHINO_INERTIA, 1)]+  , iverbHit = "thunder"+  , idesc    = "It's a struggle to move the mass and it's a reinforced concrete wall that stops it."+  }+electricAmbience = armoredSkin+  { isymbol  = 'v'+  , iname    = "static current ambience"+  , ifreq    = [(ELECTRIC_AMBIENCE, 1)]+  , iverbHit = "shortcut"+  , iaspects = [ Timeout $ (2 + 1 `d` 3) * 5+               , SetFlag Periodic, SetFlag Durable ]+  , ieffects = [RefillHP 2, Explode S_CURRENT_DISCHARGE]+  , idesc    = ""+  }+electricAmbienceRecharge = electricAmbience+  { iname    = "static current ambience"+  , ieffects = [RefillHP 1, Explode S_CURRENT_RECHARGE]+  }+robotBrain = armoredSkin+  { iname    = "robot brain"+  , ifreq    = [(S_ROBOT_BRAIN, 1)]+  , iverbHit = "outcompute"+  , iaspects = [AddSkill sk 1 | sk <- [SkMove .. SkApply]]+               ++ [AddSkill SkMove 4]  -- can move at once when waking up+               ++ [AddSkill SkAlter 1]  -- can open doors; only easiest stairs+               ++ [AddSkill SkWait 2]  -- can brace and sleep+               -- No @SkAlter@ bonus, so can only use the easiest stairs.+               ++ [AddSkill SkApply (-1)]  -- can't even eat food, but can fling+               ++ [SetFlag Durable]+  , idesc    = ""+  }+hullPlating = armoredSkin+  { iname    = "hull plating"+  , ifreq    = [(S_HULL_PLATING, 1)]+  , idesc    = ""+  }+mouthVent = armoredSkin+  { isymbol  = 'v'+  , iname    = "mouth vent"+  , ifreq    = [(S_MOUTH_VENT, 1)]+  , iflavour = zipPlain [BrMagenta]+  , iverbHit = "surprise"+  , iaspects = [ Timeout 7+               , SetFlag Periodic, SetFlag Durable ]+  , ieffects = [OneOf $+      map (\msg -> AndEffect (Explode S_SMOKE) (uncurry VerbMsg msg))+          [ ("say: Sir, your luggage has already been collected", ".")+          , ("ask: Would you kindly help me?", "")+          , ("complain: I can't reach you with this tool", "." ) ]+      ++ map Explode+             [ S_PHEROMONE, S_RHINO_HOLOGRAM, S_CURRENT_DISCHARGE+             , blastNoStatOf S_IMMOBILE, S_SPARK ]]+  , idesc    = ""+  }+dustVent = armoredSkin+  { isymbol  = 'v'+  , iname    = "vent"+  , ifreq    = [(S_DUST_VENT, 1)]+  , iflavour = zipPlain [BrCyan]+  , iverbHit = "menace"+  , iaspects = [ Timeout $ (5 + 1 `d` 3) * 5+               , SetFlag Periodic, SetFlag Durable ]+  , ieffects = [RefillHP 2, Explode S_VIOLENT_FLASH]+  , idesc    = ""+  }+fuelVent = armoredSkin+  { isymbol  = 'v'+  , iname    = "vent"+  , ifreq    = [(S_FUEL_VENT, 1)]+  , iflavour = zipPlain [BrRed]+  , iverbHit = "menace"+  , iaspects = [ Timeout $ (3 + 1 `d` 3) * 5+               , SetFlag Periodic, SetFlag Durable ]+  , ieffects = [RefillHP 2, Explode S_VIOLENT_BURNING_OIL_4]+  , idesc    = ""+  }+-- HP change varies due to body size.+--+-- This is destroyed on drop due to being an organ and so it runs+-- the @OnSmash@ effects. Due to being a condition, it's not activated+-- at actor death, avoiding a lot of spam and bringing him back to life.+geneticFlaw :: Int -> Bool -> Int -> GroupName ItemKind -> ItemKind+geneticFlaw fr badArmorMelee n grp = armoredSkin+  { isymbol  = 'F'+  , iname    = "genetic flaw"  -- keep the same as in @ifreq@, to simplify code+  , ifreq    = [(GENETIC_FLAW, fr), (grp, 1)]+  , iflavour = zipPlain [BrRed]+  , iverbHit = "flaw"+  , iweight  = 0+  , iaspects = [ AddSkill SkMaxHP (intToDice $ - n)+               , SetFlag MetaGame, SetFlag Condition ]+                   -- avoid being blamed in combat messages for bad defence,+                   -- but no CONDITION group, not to be healed too easily+               ++ [AddSkill SkWait (-1) | n >= 10]+               ++ [AddSkill SkApply (-1) | n >= 10]+               ++ if badArmorMelee+                  then [AddSkill SkArmorMelee (-20)]+                  else [AddSkill SkArmorRanged (-10)]+  , ieffects = [ OnSmash $ DropItem maxBound maxBound COrgan CONDITION+                   -- key for AI is it eliminates all impression conditions+               , OnSmash $ RefillHP n+               , OnSmash $ VerbNoLonger "undergo instant infracellular decontamination" "." ]  -- unlike the civilian version, this one is instant and the attunement is automatic and relatively quick (the usual double cooldown when equipping items again)+  , idesc    = "Nobody is perfect. At least without infracellular engineering, which is heavily regulated, insanely expensive and automatically reverted without refund before critical medical interventions. One more reason to be a good citizen, work hard and not die often. But where is the fun in that?"+  }+geneticFlaw3BadArmorMelee = geneticFlaw 1 True 3 GENETIC_FLAW_3+geneticFlaw3BadArmorRanged = geneticFlaw 3 False 3 GENETIC_FLAW_3+geneticFlaw10BadArmorMelee = geneticFlaw 1 True 10 GENETIC_FLAW_10+geneticFlaw10BadArmorRanged = geneticFlaw 3 False 10 GENETIC_FLAW_10++-- * Allure-specific backstory items++-- The name and description of each backstory item should add something+-- to the biography or character of the hero or both. However, it should+-- not constrain it so much that it conflicts with any other items.+-- E.g., 'life spent in military' is not acceptable,+-- but a spell in military is fine, just as is life spent doing+-- dangerous tasks, serious missions or giving orders.+-- The idea, in addition to avoiding inconsistency, is to let the player+-- fill in the gaps and, in effect, invent the backstories.+-- OTOH, the player needs something to work with, so a Wikipedia+-- definition of a mild vice or a veiled allusion to in-game mechanics+-- of a virtue are not enough. It's fine if some backstory+-- items are more constraining that others. Variety is good.+--+-- Make sure the effects here that not always fire don't fail with UseId+-- and so reveal the backstory item too early and without the fun.+-- The exception is fluff items, where the effects are marginal by definition.+backstoryFluffTemplate = ItemKind+  { isymbol  = '?'+  , iname    = "unrevealed rumination"+  , ifreq    = [(BACKSTORY_FLUFF_UNKNOWN, 1)]+  , iflavour = zipStory [BrBlue, Blue]+  , icount   = 1+  , irarity  = [(1, 1)]+  , iverbHit = "surprise"+  , iweight  = 0+  , idamage  = 0+  , iaspects = [ PresentAs BACKSTORY_FLUFF_UNKNOWN, SetFlag MetaGame+               , SetFlag Durable+               , SetFlag MinorAspects ]+                   -- avoid question marks by weapons in HUD, because,+                   -- unlike necklaces, these won't have stat boosts+  , ieffects = []+  , idesc    = "Not all crucial facts about a team member are remembered and revealed as soon as would be most beneficial for the team."+  , ikit     = []+  }+backstoryFluff1 = backstoryFluffTemplate+  { iname    = "\"Naughty Kid\""+  , ifreq    = [(BACKSTORY_FLUFF, 100), (BACKSTORY, 1)]+  , iaspects = [Timeout 500, SetFlag UnderMelee]+               ++ iaspects backstoryFluffTemplate+  , ieffects = [When (CalmLeq 30) $ OneOf $ map (uncurry VerbMsg)+      [ ("rage, froth-mouthed", ".")+      , ("say: Déjà vu?", "")+      , ("yell: I missed that so", "!")+      , ("hesitate for the briefest moment", ".")+      , ("inquire: in the face? in the face?", "")+      , ("lose it", ".")+      ]]+  , idesc    = "Rumination: Bad temper, anguish and uncontrollable fury blot out all other childhood memories."+  }+backstoryGoodTemplate = backstoryFluffTemplate+  { iname    = "unrevealed virtue"+  , ifreq    = [(BACKSTORY_GOOD_UNKNOWN, 1)]+  , iflavour = zipStory [cGoodEvent, cVeryGoodEvent]+  , iaspects = [ PresentAs BACKSTORY_GOOD_UNKNOWN, SetFlag MetaGame+               , SetFlag Durable, SetFlag MinorAspects ]+  }+backstoryGood1 = backstoryGoodTemplate+  { iname    = "\"Zero g Champ\""+  , ifreq    = [(BACKSTORY_GOOD, 100), (BACKSTORY, 1)]+  , iaspects = [Timeout 1000, SetFlag UnderRanged, SetFlag Unique]+               ++ iaspects backstoryGoodTemplate+                 -- unique, so that team not overpowered+  , ieffects = [toOrganGood S_RANGED_DEFLECTING 10]+                  -- rare, but long lasting+  , idesc    = "Virtue: Years of training for null gravity squash tournaments unexpectedly pay off. At this very distance and speed, trajectory angles are obvious and lounging and feinting comes naturally."+  }+backstoryGood2 = backstoryGoodTemplate+  { iname    = "\"Loyalty ritual\""+  , ifreq    = [(BACKSTORY_GOOD, 100), (BACKSTORY, 1)]+  , iaspects = [Timeout 200, SetFlag Periodic]+               ++ iaspects backstoryGoodTemplate+  , ieffects = [Impress `AndEffect` VerbMsg "mumble repeatedly" "."]+  , idesc    = "Virtue: Reciting, every once in a while, the names of all the people your life directly depends on is a proven loyalty-affirming custom. It also lets you pinpoint the moment you start hating a teammate."  -- 'ritual' is exotic enough to add to biography+  }+backstoryGood3 = backstoryGoodTemplate+  { iname    = "\"Bravery\""+  , ifreq    = [(BACKSTORY_GOOD, 100), (BACKSTORY, 1)]+  , iaspects = [Timeout 1, SetFlag UnderMelee]+               ++ iaspects backstoryGoodTemplate+  , ieffects = [When (CalmLeq 2) $ When (TriggeredBy ActivationMeleeable)+                $ SeqEffect+                    [ Recharge 4 20+                    , RefillCalm 2+                    ]]+                 -- @TriggeredBy@ condition prevents micro-management+  , idesc    = "Virtue: Several years of life threatening events leave either a trauma or a lesson. The lesson is, when fear is the strongest, turn away from yourself and fiercely focus on your mission."+  }+backstoryBadTemplate = backstoryFluffTemplate+  { iname    = "unrevealed vice"+  , ifreq    = [(BACKSTORY_BAD_UNKNOWN, 1)]+  , iflavour = zipStory [cBadEvent, cVeryBadEvent]+  , iaspects = [ PresentAs BACKSTORY_BAD_UNKNOWN, SetFlag MetaGame+               , SetFlag Durable, SetFlag MinorAspects ]+  }+backstoryBad1 = backstoryBadTemplate+  { iname    = "\"Drug addiction\""+  , ifreq    = [(BACKSTORY_BAD, 100), (BACKSTORY, 1)]+  , iaspects = [Timeout 200, SetFlag Periodic, SetFlag Unique]+               ++ iaspects backstoryBadTemplate+  , ieffects = [When (CalmLeq 40) $ When (CalmGeq 20) $ SeqEffect+      [ toOrganBad S_SLOWED (3 + 1 `d` 3)+      , toOrganBad S_FRENZIED (40 + 1 `d` 10)+      , toOrganGood S_STRENGTHENED (5 + 1 `d` 5) ]]  -- to short to compensate+  , idesc    = "Vice: The small pill that distracts from mild anxiety also distracts from survival and responsibility towards teammates."+  }+backstoryBad2 = backstoryBadTemplate+  { iname    = "\"Overconfidence\""+  , ifreq    = [(BACKSTORY_BAD, 100), (BACKSTORY, 1)]+  , iaspects = [Timeout 200, SetFlag UnderMelee]+               ++ iaspects backstoryBadTemplate+  , ieffects = [When (CalmGeq 80) $ SeqEffect+      [ toOrganBad S_WEAKENED (20 + 1 `d` 5)+      , toOrganBad S_DEAF (20 + 1 `d` 5) ]]+  , idesc    = "Vice: Never underestimate a maddened, towering abomination, when you only have a stick to fend it off with. Bonus advice: the fact you excelled in school athletics doesn't mean every power in the universe is going to read you body language and cower."+  }+backstoryBad3 = backstoryBadTemplate+  { iname    = "\"Arrogance\""+  , ifreq    = [(BACKSTORY_BAD, 100), (BACKSTORY, 1)]+  , iaspects = [Timeout 200, SetFlag UnderMelee]+               ++ iaspects backstoryBadTemplate+  , ieffects = [When (CalmGeq 80) $ SeqEffect+      [ Discharge 3 40+      , Yell+      , OneOf $ map (uncurry VerbMsg)+          [ ("yell: How dare you touch me?", "")+          , ("yell: Do you know who I am?", "")+          , ("yell: Calling my lawyer", "!")+          ] ]]+  , idesc    = "Vice: Privilege can be a boon, but if taken for granted, it can be a doom."+  }+backstoryBad4 = backstoryBadTemplate+  { iname    = "\"Alcoholism\""+  , ifreq    = [(BACKSTORY_BAD, 100), (BACKSTORY, 1)]+  , iaspects = [ Timeout 200, SetFlag Periodic+               , SetFlag Unique ]  -- a tragic backstory and so unique+               ++ iaspects backstoryBadTemplate+  , ieffects = [AtMostOneOf  -- simple but pessimistic probability; not accurate+      [ DestroyItem 1 1 CStash ALCOHOL+          -- this is not drinking, but smashing; say, inner fight, won;+          -- @ConsumeItems [(1, ALCOHOL)] []@ would drink, but not from stash+          -- and only for alcohols that are durable, which is unlikely+        `OrEffect`  -- only create if none available+        SeqEffect [ CreateItem (Just 1) CStash ALCOHOL timerNone+                  , VerbMsg "explain: Some moonshine from gathered scraps, just in case" "." ]+      , NopEffect  -- always fails, no identification+      ]]+  , idesc    = "Vice: Adventurers that are abstaining alcoholics are double heroes, even if they too may trip sometimes. That doesn't make alcoholism a virtue, though."+  }+backstoryMixedTemplate = backstoryFluffTemplate+  { iname    = "unrevealed twist"+  , ifreq    = [(BACKSTORY_MIXED_UNKNOWN, 1)]+  , iflavour = zipStory [cRisk, cGraveRisk]+  , iaspects = [ PresentAs BACKSTORY_MIXED_UNKNOWN, SetFlag MetaGame+               , SetFlag Durable, SetFlag MinorAspects ]+  }+backstoryMixed1 = backstoryMixedTemplate+  { iname    = "\"Heavy eyes\""+  , ifreq    = [(BACKSTORY_MIXED, 100), (BACKSTORY, 1)]+  , iaspects = [Timeout 100, SetFlag Periodic]+               ++ iaspects backstoryMixedTemplate+  , ieffects = [AtMostOneOf [ PutToSleep+                            , NopEffect+                            , NopEffect+                            ]]+                  -- mixed, sleep refills Calm fully, but hobbles the actor+  , idesc    = "Twist: Can sleep anywhere, any time. The catch: sleeps anywhere, any time."+  }+backstoryMixed2 = backstoryMixedTemplate+  { iname    = "\"Too Young to Die\""+  , ifreq    = [(BACKSTORY_MIXED, 100), (BACKSTORY, 1)]+  , iaspects = [Timeout 1, SetFlag UnderRanged, SetFlag UnderMelee]+               ++ iaspects backstoryMixedTemplate+  , ieffects = [When (HpLeq 10) $ Unless (TriggeredBy ActivationTrigger)+                $ SeqEffect+      [RefillCalm (-10), toOrganGood S_HASTED 1, Recharge 1 20]]+        -- mixed: Calm lost but fury helps survive; may fire once per turn+        -- @TriggeredBy@ condition prevents micro-management+  , idesc    = "Twist: Despair, fury, denial. Beastly reactions to approaching death. This is unforgivable in space, especially when getting acquainted with dying is so accessible, for a generation already, either via hibernation and nanobot revival or mental exercise culminating in writing a will to be notarized Earth-side."  -- hint that the nanobot revival heroes use all the time may have stemmed from space travel hibernation research; if so, the culture followed, but some youngsters are lazy wimps+  }+backstoryNeutralTemplate = backstoryFluffTemplate+  { iname    = "unrevealed quirk"+  , ifreq    = [(BACKSTORY_NEUTRAL_UNKNOWN, 1)]+  , iflavour = zipStory [cNeutralEvent, cRareNeutralEvent]+  , iaspects = [ PresentAs BACKSTORY_NEUTRAL_UNKNOWN, SetFlag MetaGame+               , SetFlag Durable, SetFlag MinorAspects ]+  }+backstoryNeutral1 = backstoryNeutralTemplate+  { iname    = "\"Letting Go\""+  , ifreq    = [(BACKSTORY_NEUTRAL, 100), (BACKSTORY, 1)]+  , ieffects = [ OnSmash (Explode S_YOUTH_SPRINKLE)  -- may hit foes as well+               , Unless (TriggeredBy ActivationTrigger)+                 $ VerbMsg "laugh warmly" "." ]+                   -- never activated, but prevents premature identification+  , idesc    = "Quirk: Dying beautifully is an art that takes a lifetime to master and leaves spectators peaceful and uplifted. That's true even for clinical death, potentially reversible with nano medbot treatment back in town."+  }+-- Both can be moved to Mixed if less numerous, even though the effects mild.+backstoryNeutral2 = backstoryNeutralTemplate+  { iname    = "\"Mood swings\""+  , ifreq    = [(BACKSTORY_NEUTRAL, 100), (BACKSTORY, 1)]+  , iaspects = [Timeout 100, SetFlag Periodic]+               ++ iaspects backstoryMixedTemplate+  , ieffects = [When (HpLeq 50) $ OneOf [RefillCalm 20, RefillCalm (-20)]]+  , idesc    = "Quirk: Hormonal imbalances make it hard to compensate for natural neural system unsteadiness."+  }+backstoryNeutral3 = backstoryNeutralTemplate+  { iname    = "\"Cracking under stress\""+  , ifreq    = [(BACKSTORY_NEUTRAL, 100), (BACKSTORY, 1)]+  , iaspects = [SetFlag UnderRanged, SetFlag UnderMelee]+               ++ iaspects backstoryMixedTemplate+  , ieffects = [When (HpLeq 50) $ Unless (TriggeredBy ActivationTrigger)+                $ OneOf [RefillCalm 10, RefillCalm (-10)]]+                  -- @TriggeredBy@ condition prevents micro-management+  , idesc    = "Quirk: Not everyone needs to be impassive. A brush with death may result in panic today, but in cheering up and rallying the whole team tomorrow. However, if the cost of bad outcomes is prohibitive, exposed posts and front line assignments are better avoided."  -- 'the whole team' is an exaggeration, but it's in-character here+  }
GameDefinition/Content/ItemKindTemporary.hs view
@@ -1,12 +1,19 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Temporary aspect pseudo-item definitions. module Content.ItemKindTemporary-  ( temporaries+  ( -- * Group name patterns+    pattern S_IMMOBILE, pattern S_PACIFIED, pattern S_IRREPLACEABLE, pattern S_RETAINING, pattern S_IMPATIENT, pattern S_DISPOSSESSED, pattern S_WITHHOLDING, pattern S_PARSIMONIOUS+  , pattern S_MORE_MOBILE, pattern S_MORE_COMBATIVE, pattern S_MORE_DISPLACING, pattern S_MORE_MODIFYING, pattern S_MORE_PATIENT, pattern S_MORE_TIDY, pattern S_MORE_PROJECTING, pattern S_MORE_PRACTICAL+  , pattern S_STRENGTHENED, pattern S_WEAKENED, pattern S_PROTECTED_FROM_MELEE, pattern S_PROTECTED_FROM_RANGED, pattern S_DEFENSELESS, pattern S_RESOLUTE, pattern S_HASTED, pattern S_SLOWED, pattern S_FAR_SIGHTED, pattern S_BLIND, pattern S_KEEN_SMELLING, pattern S_FOUL_SMELLING, pattern S_ROSE_SMELLING, pattern S_RANGED_DEFLECTING, pattern S_MELEE_DEFLECTING, pattern S_SHINY_EYED, pattern S_DEAFENED, pattern S_DEAF, pattern S_DRUNK, pattern S_FRENZIED, pattern S_REGENERATING, pattern S_POISONED, pattern S_SLOW_RESISTANT, pattern S_POISON_RESISTANT+  , pattern S_PAINTED+  , temporariesGNSingleton, noStatGN, bonusStatGN+  , -- * Content+    temporaries   ) where  import Prelude ()@@ -20,117 +27,205 @@ import Game.LambdaHack.Definition.Defs import Game.LambdaHack.Definition.Flavour +-- * Group name patterns++noStatGN :: [GroupName ItemKind]+noStatGN =+       [S_IMMOBILE, S_PACIFIED, S_IRREPLACEABLE, S_RETAINING, S_IMPATIENT, S_DISPOSSESSED, S_WITHHOLDING, S_PARSIMONIOUS]++bonusStatGN :: [GroupName ItemKind]+bonusStatGN =+       [S_MORE_MOBILE, S_MORE_COMBATIVE, S_MORE_DISPLACING, S_MORE_MODIFYING, S_MORE_PATIENT, S_MORE_TIDY, S_MORE_PROJECTING, S_MORE_PRACTICAL]++temporariesGNSingleton :: [GroupName ItemKind]+temporariesGNSingleton =+       [S_STRENGTHENED, S_WEAKENED, S_PROTECTED_FROM_MELEE, S_PROTECTED_FROM_RANGED, S_DEFENSELESS, S_RESOLUTE, S_HASTED, S_SLOWED, S_FAR_SIGHTED, S_BLIND, S_KEEN_SMELLING, S_FOUL_SMELLING, S_ROSE_SMELLING, S_RANGED_DEFLECTING, S_MELEE_DEFLECTING, S_SHINY_EYED, S_DEAFENED, S_DEAF, S_DRUNK, S_FRENZIED, S_REGENERATING, S_POISONED, S_SLOW_RESISTANT, S_POISON_RESISTANT]+    ++ noStatGN ++ bonusStatGN+    ++ [S_PAINTED]++pattern S_IMMOBILE, S_PACIFIED, S_IRREPLACEABLE, S_RETAINING, S_IMPATIENT, S_DISPOSSESSED, S_WITHHOLDING, S_PARSIMONIOUS :: GroupName ItemKind++pattern S_MORE_MOBILE, S_MORE_COMBATIVE, S_MORE_DISPLACING, S_MORE_MODIFYING, S_MORE_PATIENT, S_MORE_TIDY, S_MORE_PROJECTING, S_MORE_PRACTICAL :: GroupName ItemKind++pattern S_STRENGTHENED, S_WEAKENED, S_PROTECTED_FROM_MELEE, S_PROTECTED_FROM_RANGED, S_DEFENSELESS, S_RESOLUTE, S_HASTED, S_SLOWED, S_FAR_SIGHTED, S_BLIND, S_KEEN_SMELLING, S_FOUL_SMELLING, S_ROSE_SMELLING, S_RANGED_DEFLECTING, S_MELEE_DEFLECTING, S_SHINY_EYED, S_DEAFENED, S_DEAF, S_DRUNK, S_FRENZIED, S_REGENERATING, S_POISONED, S_SLOW_RESISTANT, S_POISON_RESISTANT :: GroupName ItemKind++pattern S_PAINTED :: GroupName ItemKind++pattern S_STRENGTHENED = GroupName "strengthened"+pattern S_WEAKENED = GroupName "weakened"+pattern S_PROTECTED_FROM_MELEE = GroupName "protected from melee"+pattern S_PROTECTED_FROM_RANGED = GroupName "protected from ranged"+pattern S_DEFENSELESS = GroupName "defenseless"+pattern S_RESOLUTE = GroupName "resolute"+pattern S_HASTED = GroupName "hasted"+pattern S_SLOWED = GroupName "slowed"+pattern S_FAR_SIGHTED = GroupName "far-sighted"+pattern S_BLIND = GroupName "blind"+pattern S_KEEN_SMELLING = GroupName "keen-smelling"+pattern S_FOUL_SMELLING = GroupName "foul-smelling"+pattern S_ROSE_SMELLING = GroupName "rose-smelling"+pattern S_RANGED_DEFLECTING = GroupName "ranged-deflecting"+pattern S_MELEE_DEFLECTING = GroupName "melee-deflecting"+pattern S_SHINY_EYED = GroupName "shiny-eyed"+pattern S_DEAFENED = GroupName "deafened"+pattern S_DEAF = GroupName "deaf"+pattern S_DRUNK = GroupName "drunk"+pattern S_FRENZIED = GroupName "frenzied"+pattern S_REGENERATING = GroupName "regenerating"+pattern S_POISONED = GroupName "poisoned"+pattern S_SLOW_RESISTANT = GroupName "slow resistant"+pattern S_POISON_RESISTANT = GroupName "poison resistant"+pattern S_IMMOBILE = GroupName "immobile"+pattern S_PACIFIED = GroupName "pacified"+pattern S_IRREPLACEABLE = GroupName "irreplaceable"+pattern S_RETAINING = GroupName "retaining"+pattern S_IMPATIENT = GroupName "impatient"+pattern S_DISPOSSESSED = GroupName "dispossessed"+pattern S_WITHHOLDING = GroupName "withholding"+pattern S_PARSIMONIOUS = GroupName "parsimonious"+pattern S_MORE_MOBILE = GroupName "super-mobile"+pattern S_MORE_COMBATIVE = GroupName "super-combative"+pattern S_MORE_DISPLACING = GroupName "super-displacing"+pattern S_MORE_MODIFYING = GroupName "super-modifying"+pattern S_MORE_PATIENT = GroupName "super-patient"+pattern S_MORE_TIDY = GroupName "super-tidy"+pattern S_MORE_PROJECTING = GroupName "super-projecting"+pattern S_MORE_PRACTICAL = GroupName "super-practical"++-- ** Allure-specific+pattern S_PAINTED = GroupName "painted"++-- * Content+ temporaries :: [ItemKind] temporaries =-  [tmpStrengthened, tmpWeakened, tmpProtectedMelee, tmpProtectedRanged, tmpVulnerable, tmpResolute, tmpFast20, tmpSlow10, tmpFarSighted, tmpBlind, tmpKeenSmelling, tmpFoulSmelling, tmpRoseSmelling, tmpNoctovision, tmpDeafened, tmpDeaf, tmpDrunk, tmpBonusSkAggresion, tmpNoSkMove, tmpNoSkMelee, tmpNoSkDisplace, tmpNoSkAlter, tmpNoSkWait, tmpNoSkMoveItem, tmpNoSkProject, tmpNoSkApply, tmpBonusSkMove, tmpBonusSkMelee, tmpBonusSkDisplace, tmpBonusSkAlter, tmpBonusSkWait, tmpBonusSkMoveItem, tmpBonusSkProject, tmpBonusSkApply, tmpRegenerating, tmpPoisoned, tmpSlow10Resistant, tmpPoisonResistant]+  [tmpStrengthened, tmpWeakened, tmpProtectedMelee, tmpProtectedRanged, tmpDefenseless, tmpResolute, tmpFast20, tmpSlow10, tmpFarSighted, tmpBlind, tmpKeenSmelling, tmpFoulSmelling, tmpRoseSmelling, tmpRangedDeflecting, tmpMeleeDeflecting, tmpNoctovision, tmpDeafened, tmpDeaf, tmpDrunk, tmpBonusSkAggresion, tmpRegenerating, tmpPoisoned, tmpSlow10Resistant, tmpPoisonResistant, tmpNoSkMove, tmpNoSkMelee, tmpNoSkDisplace, tmpNoSkAlter, tmpNoSkWait, tmpNoSkMoveItem, tmpNoSkProject, tmpNoSkApply, tmpBonusSkMove, tmpBonusSkMelee, tmpBonusSkDisplace, tmpBonusSkAlter, tmpBonusSkWait, tmpBonusSkMoveItem, tmpBonusSkProject, tmpBonusSkApply]+  ++ [tmpPainted] -tmpStrengthened,    tmpWeakened, tmpProtectedMelee, tmpProtectedRanged, tmpVulnerable, tmpResolute, tmpFast20, tmpSlow10, tmpFarSighted, tmpBlind, tmpKeenSmelling, tmpFoulSmelling, tmpRoseSmelling, tmpNoctovision, tmpDeafened, tmpDeaf, tmpDrunk, tmpBonusSkAggresion, tmpNoSkMove, tmpNoSkMelee, tmpNoSkDisplace, tmpNoSkAlter, tmpNoSkWait, tmpNoSkMoveItem, tmpNoSkProject, tmpNoSkApply, tmpBonusSkMove, tmpBonusSkMelee, tmpBonusSkDisplace, tmpBonusSkAlter, tmpBonusSkWait, tmpBonusSkMoveItem, tmpBonusSkProject, tmpBonusSkApply, tmpRegenerating, tmpPoisoned, tmpSlow10Resistant, tmpPoisonResistant :: ItemKind+tmpStrengthened,    tmpWeakened, tmpProtectedMelee, tmpProtectedRanged, tmpDefenseless, tmpResolute, tmpFast20, tmpSlow10, tmpFarSighted, tmpBlind, tmpKeenSmelling, tmpFoulSmelling, tmpRoseSmelling, tmpRangedDeflecting, tmpMeleeDeflecting, tmpNoctovision, tmpDeafened, tmpDeaf, tmpDrunk, tmpBonusSkAggresion, tmpRegenerating, tmpPoisoned, tmpSlow10Resistant, tmpPoisonResistant, tmpNoSkMove, tmpNoSkMelee, tmpNoSkDisplace, tmpNoSkAlter, tmpNoSkWait, tmpNoSkMoveItem, tmpNoSkProject, tmpNoSkApply, tmpBonusSkMove, tmpBonusSkMelee, tmpBonusSkDisplace, tmpBonusSkAlter, tmpBonusSkWait, tmpBonusSkMoveItem, tmpBonusSkProject, tmpBonusSkApply :: ItemKind +tmpPainted :: ItemKind+ -- The @name@ is be used in item description, so it should be an adjective -- describing the temporary set of aspects. -- The messages are needed also under @OnSmash@ to display when item removed -- via @DropItem@ and not via natural periodic activation.-tmpAspects :: Text -> [Aspect] -> ItemKind-tmpAspects name aspects = ItemKind-  { isymbol  = '+'-  , iname    = name-  , ifreq    = [(toGroupName name, 1), ("condition", 1)]-  , iflavour = zipPlain [BrWhite]-  , icount   = 1-  , irarity  = [(1, 1)]-  , iverbHit = "affect"-  , iweight  = 0-  , idamage  = 0-  , iaspects = -- timeout is 0; activates and vanishes soon,-               -- depending on initial timer setting-               aspects ++ [SetFlag Periodic, SetFlag Fragile, SetFlag Condition]-  , ieffects = [ OnSmash $ verbMsgLess name  -- announce partial neutralization-               -- not spamming for normal periodic wear each turn-               , OnSmash $ verbMsgNoLonger name  -- for forced neutralization-               , verbMsgNoLonger name ]  -- for periodic wear of last copy-  , idesc    = ""  -- no description needed; powers are enough-  , ikit     = []-  }+tmpAspects :: GroupName ItemKind -> [Aspect] -> ItemKind+tmpAspects grp aspects =+  let name = fromGroupName grp  -- @iname@ must match @ifreq@, see @myBadGrps@+  in ItemKind+    { isymbol  = '+'+    , iname    = name+    , ifreq    = [(grp, 1), (CONDITION, 1)]+    , iflavour = zipPlain [BrWhite]+    , icount   = 1+    , irarity  = [(1, 1)]+    , iverbHit = "affect"+    , iweight  = 0+    , idamage  = 0+    , iaspects = -- timeout is 0; activates and vanishes soon,+                 -- depending on initial timer setting+                 aspects+                 ++ [SetFlag Periodic, SetFlag Fragile, SetFlag Condition]+    , ieffects = [ OnSmash $ verbMsgLess name+                   -- announce partial neutralization, but don't spam+                   -- about normal periodic wear each turn+                 , OnSmash $ verbMsgNoLonger name  -- for forced neutralization+                 , verbMsgNoLonger name ]  -- for periodic wear of last copy+    , idesc    = ""  -- no description needed; powers are enough+    , ikit     = []+    } -tmpEffects :: Text -> Dice -> [Effect] -> ItemKind-tmpEffects name icount effects =-  let tmp = tmpAspects name []+tmpEffects :: GroupName ItemKind -> Dice -> [Effect] -> ItemKind+tmpEffects grp icount effects =+  let tmp = tmpAspects grp []   in tmp { icount          , ieffects = effects ++ ieffects tmp          } -tmpStrengthened = tmpAspects "strengthened" [AddSkill SkHurtMelee 20]-tmpWeakened = tmpAspects "weakened"+tmpStrengthened = tmpAspects S_STRENGTHENED [AddSkill SkHurtMelee 20]+tmpWeakened = tmpAspects S_WEAKENED                          [AddSkill SkHurtMelee (-30)]  -- don't cancel out ^-tmpProtectedMelee = tmpAspects "protected from melee"+tmpProtectedMelee = tmpAspects S_PROTECTED_FROM_MELEE                                [AddSkill SkArmorMelee 50]-tmpProtectedRanged = tmpAspects "protected from ranged"+tmpProtectedRanged = tmpAspects S_PROTECTED_FROM_RANGED                                 [AddSkill SkArmorRanged 25]-tmpVulnerable = tmpAspects "painted" [ AddSkill SkArmorMelee (-50)-                                     , AddSkill SkArmorRanged (-25)-                                     , AddSkill SkShine 1 ]-tmpResolute = tmpAspects "resolute" [AddSkill SkMaxCalm 60]-tmpFast20 = tmpAspects "hasted" [AddSkill SkSpeed 20]-tmpSlow10 = tmpAspects "slowed" [AddSkill SkSpeed (-10)]-tmpFarSighted = tmpAspects "far-sighted" [AddSkill SkSight 5]-tmpBlind = tmpAspects "blind" [AddSkill SkSight (-99)]-tmpKeenSmelling = tmpAspects "keen-smelling" [AddSkill SkSmell 2]-tmpFoulSmelling = tmpAspects "foul-smelling" [AddSkill SkOdor 2]-tmpRoseSmelling = tmpAspects "rose-smelling" [AddSkill SkOdor (-4)]-tmpNoctovision = tmpAspects "shiny-eyed" [AddSkill SkNocto 2]-tmpDeafened = tmpAspects "deafened" [AddSkill SkHearing (-10)]-tmpDeaf = tmpAspects "deaf" [AddSkill SkHearing (-99)]-tmpDrunk = tmpAspects "drunk" [ AddSkill SkHurtMelee 30  -- fury-                              , AddSkill SkArmorMelee (-20)-                              , AddSkill SkArmorRanged (-20)-                              , AddSkill SkSight (-8)-                              ]+tmpDefenseless = tmpAspects S_DEFENSELESS [ AddSkill SkArmorMelee (-50)+                                          , AddSkill SkArmorRanged (-25) ]+tmpResolute = tmpAspects S_RESOLUTE [AddSkill SkMaxCalm 60]+tmpFast20 = tmpAspects S_HASTED [AddSkill SkSpeed 20]+tmpSlow10 = tmpAspects S_SLOWED [AddSkill SkSpeed (-10)]+tmpFarSighted = tmpAspects S_FAR_SIGHTED [AddSkill SkSight 5]+tmpBlind = tmpAspects S_BLIND [ AddSkill SkSight (-99)+                              , AddSkill SkArmorMelee (-30) ]+tmpKeenSmelling = tmpAspects S_KEEN_SMELLING [AddSkill SkSmell 2]+tmpFoulSmelling = tmpAspects S_FOUL_SMELLING [AddSkill SkOdor 2]+tmpRoseSmelling = tmpAspects S_ROSE_SMELLING [AddSkill SkOdor (-4)]+tmpRangedDeflecting =+  tmpAspects S_RANGED_DEFLECTING [AddSkill SkDeflectRanged 1]+tmpMeleeDeflecting =+  tmpAspects S_MELEE_DEFLECTING [AddSkill SkDeflectMelee 1]+tmpNoctovision = tmpAspects S_SHINY_EYED [AddSkill SkNocto 2]+tmpDeafened = tmpAspects S_DEAFENED [AddSkill SkHearing (-6)]+tmpDeaf = tmpAspects S_DEAF [ AddSkill SkHearing (-99)+                            , AddSkill SkArmorMelee (-30) ]+tmpDrunk = tmpAspects S_DRUNK [ AddSkill SkHurtMelee 30  -- fury+                              , AddSkill SkArmorRanged (-30)+                              , AddSkill SkSight (-8) ]+ tmpBonusSkAggresion =-  tmpAspects "frenzied" [AddSkill SkAggression 5]+  tmpAspects S_FRENZIED [ AddSkill SkAggression 5+                        , AddSkill SkArmorMelee (-30) ] +tmpRegenerating =+  tmpEffects S_REGENERATING (4 + 1 `d` 2) [RefillHP 1]+tmpPoisoned =+  tmpEffects S_POISONED (3 + 1 `d` 2) [RefillHP (-1)]+tmpSlow10Resistant =+  tmpEffects S_SLOW_RESISTANT (8 + 1 `d` 4)+             [DropItem 1 1 COrgan S_SLOWED]+tmpPoisonResistant =+  tmpEffects S_POISON_RESISTANT (8 + 1 `d` 4)+             [DropItem 1 maxBound COrgan S_POISONED]+ tmpNoSkMove =-  tmpAspects "immobile" [AddSkill SkMove (-99)]+  tmpAspects S_IMMOBILE [AddSkill SkMove (-99)] tmpNoSkMelee =-  tmpAspects "pacified" [AddSkill SkMelee (-99)]+  tmpAspects S_PACIFIED [AddSkill SkMelee (-99)] tmpNoSkDisplace =-  tmpAspects "irreplaceable" [AddSkill SkDisplace (-99)]+  tmpAspects S_IRREPLACEABLE [AddSkill SkDisplace (-99)] tmpNoSkAlter =-  tmpAspects "retaining" [AddSkill SkAlter (-99)]+  tmpAspects S_RETAINING [AddSkill SkAlter (-99)] tmpNoSkWait =-  tmpAspects "impatient" [AddSkill SkWait (-99)]+  tmpAspects S_IMPATIENT [AddSkill SkWait (-99)] tmpNoSkMoveItem =-  tmpAspects "dispossessed" [AddSkill SkMoveItem (-99)]+  tmpAspects S_DISPOSSESSED [AddSkill SkMoveItem (-99)] tmpNoSkProject =-  tmpAspects "withholding" [AddSkill SkProject (-99)]+  tmpAspects S_WITHHOLDING [AddSkill SkProject (-99)] tmpNoSkApply =-  tmpAspects "parsimonious" [AddSkill SkApply (-99)]+  tmpAspects S_PARSIMONIOUS [AddSkill SkApply (-99)]  tmpBonusSkMove =-  tmpAspects "more mobile" [AddSkill SkMove 5]+  tmpAspects S_MORE_MOBILE [AddSkill SkMove 5] tmpBonusSkMelee =-  tmpAspects "more combative" [AddSkill SkMelee 5]+  tmpAspects S_MORE_COMBATIVE [AddSkill SkMelee 5] tmpBonusSkDisplace =-  tmpAspects "more displacing" [AddSkill SkDisplace 5]+  tmpAspects S_MORE_DISPLACING [AddSkill SkDisplace 5] tmpBonusSkAlter =-  tmpAspects "more altering" [AddSkill SkAlter 5]+  tmpAspects S_MORE_MODIFYING [AddSkill SkAlter 5] tmpBonusSkWait =-  tmpAspects "more patient" [AddSkill SkWait 5]+  tmpAspects S_MORE_PATIENT [AddSkill SkWait 5] tmpBonusSkMoveItem =-  tmpAspects "more tidy" [AddSkill SkMoveItem 5]+  tmpAspects S_MORE_TIDY [AddSkill SkMoveItem 5] tmpBonusSkProject =-  tmpAspects "more projecting" [AddSkill SkProject 8]-    -- TODO: 11, but let player control potion throwing by henchmen;+  tmpAspects S_MORE_PROJECTING [AddSkill SkProject 8]+    -- TODO: 11, but let player control potion throwing by non-pointmen;     -- beware also of capReinforced and other sources of the skill tmpBonusSkApply =-  tmpAspects "more practical" [AddSkill SkApply 5]+  tmpAspects S_MORE_PRACTICAL [AddSkill SkApply 5] -tmpRegenerating =-  tmpEffects "regenerating" (4 + 1 `d` 2) [RefillHP 1]-tmpPoisoned =-  tmpEffects "poisoned" (4 + 1 `d` 2) [RefillHP (-1)]-tmpSlow10Resistant =-  tmpEffects "slow resistant" (8 + 1 `d` 4)-             [DropItem 1 1 COrgan "slowed"]-tmpPoisonResistant =-  tmpEffects "poison resistant" (8 + 1 `d` 4)-             [DropItem 1 maxBound COrgan "poisoned"]+-- ** Allure-specific++tmpPainted = tmpAspects S_PAINTED [ AddSkill SkArmorMelee (-50)+                                  , AddSkill SkArmorRanged (-25)+                                  , AddSkill SkShine 1 ]
GameDefinition/Content/ModeKind.hs view
@@ -1,29 +1,80 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE. -- -- | Game mode definitions. module Content.ModeKind-  ( content+  ( -- * Group names+    groupNamesSingleton, groupNames+  , -- * Content+    content+#ifdef EXPOSE_INTERNAL+  -- * Group name patterns+  , pattern RAID, pattern BRAWL, pattern LONG, pattern CRAWL, pattern FOGGY, pattern SHOOTOUT, pattern PERILOUS, pattern HUNT, pattern NIGHT, pattern ESCAPE, pattern BURNING, pattern ZOO, pattern RANGED, pattern AMBUSH, pattern SAFARI, pattern DIG, pattern SEE, pattern SHORT, pattern CRAWL_EMPTY, pattern CRAWL_SURVIVAL, pattern SAFARI_SURVIVAL, pattern BATTLE, pattern BATTLE_DEFENSE, pattern BATTLE_SURVIVAL, pattern DEFENSE, pattern DEFENSE_EMPTY+#endif   ) where  import Prelude ()  import Game.LambdaHack.Core.Prelude +import qualified Data.Text as T++import Content.CaveKind hiding (content, groupNames, groupNamesSingleton)+import Content.ItemKindActor import Content.ModeKindPlayer-import Game.LambdaHack.Content.CaveKind (CaveKind)+import Game.LambdaHack.Content.CaveKind (CaveKind, pattern DEFAULT_RANDOM) import Game.LambdaHack.Content.ModeKind import Game.LambdaHack.Core.Dice import Game.LambdaHack.Definition.Defs +-- * Group name patterns++groupNamesSingleton :: [GroupName ModeKind]+groupNamesSingleton =+       [RAID, BRAWL, LONG, CRAWL, FOGGY, SHOOTOUT, PERILOUS, HUNT, NIGHT, ESCAPE, BURNING, ZOO, RANGED, AMBUSH, SAFARI, DIG, SEE, SHORT, CRAWL_EMPTY, CRAWL_SURVIVAL, SAFARI_SURVIVAL, BATTLE, BATTLE_DEFENSE, BATTLE_SURVIVAL, DEFENSE, DEFENSE_EMPTY]++pattern RAID, BRAWL, LONG, CRAWL, FOGGY, SHOOTOUT, PERILOUS, HUNT, NIGHT, ESCAPE, BURNING, ZOO, RANGED, AMBUSH, SAFARI, DIG, SEE, SHORT, CRAWL_EMPTY, CRAWL_SURVIVAL, SAFARI_SURVIVAL, BATTLE, BATTLE_DEFENSE, BATTLE_SURVIVAL, DEFENSE, DEFENSE_EMPTY :: GroupName ModeKind++groupNames :: [GroupName ModeKind]+groupNames = [NO_CONFIRMS]++pattern RAID = GroupName "raid"+pattern BRAWL = GroupName "brawl"+pattern LONG = GroupName "long crawl"+pattern CRAWL = GroupName "crawl"+pattern FOGGY = GroupName "foggy shootout"+pattern SHOOTOUT = GroupName "shootout"+pattern PERILOUS = GroupName "perilous hunt"+pattern HUNT = GroupName "hunt"+pattern NIGHT = GroupName "night escape"+pattern ESCAPE = GroupName "escape"+pattern BURNING = GroupName "burning zoo"+pattern ZOO = GroupName "zoo"+pattern RANGED = GroupName "ranged ambush"+pattern AMBUSH = GroupName "ambush"+pattern SAFARI = GroupName "safari"+pattern DIG = GroupName "dig"+pattern SEE = GroupName "see"+pattern SHORT = GroupName "short"+pattern CRAWL_EMPTY = GroupName "crawlEmpty"  -- only the first word matters+pattern CRAWL_SURVIVAL = GroupName "crawlSurvival"+pattern SAFARI_SURVIVAL = GroupName "safariSurvival"+pattern BATTLE = GroupName "battle"+pattern BATTLE_DEFENSE = GroupName "battleDefense"+pattern BATTLE_SURVIVAL = GroupName "battleSurvival"+pattern DEFENSE = GroupName "defense"+pattern DEFENSE_EMPTY = GroupName "defenseEmpty"++-- * Content+ content :: [ModeKind] content =-  [raid, brawl, shootout, hunt, escape, zoo, ambush, crawl, crawlEmpty, crawlSurvival, dig, see, safari, safariSurvival, battle, battleDefense, battleSurvival, defense, defenseEmpty, screensaverRaid, screensaverBrawl, screensaverShootout, screensaverHunt, screensaverEscape, screensaverZoo, screensaverAmbush, screensaverCrawl, screensaverSafari]+  [raid, brawl, crawl, shootout, hunt, escape, zoo, ambush, safari, dig, see, short, crawlEmpty, crawlSurvival, safariSurvival, battle, battleDefense, battleSurvival, defense, defenseEmpty, screensaverRaid, screensaverBrawl, screensaverCrawl, screensaverShootout, screensaverHunt, screensaverEscape, screensaverZoo, screensaverAmbush, screensaverSafari] -raid,    brawl, shootout, hunt, escape, zoo, ambush, crawl, crawlEmpty, crawlSurvival, dig, see, safari, safariSurvival, battle, battleDefense, battleSurvival, defense, defenseEmpty, screensaverRaid, screensaverBrawl, screensaverShootout, screensaverHunt, screensaverEscape, screensaverZoo, screensaverAmbush, screensaverCrawl, screensaverSafari :: ModeKind+raid,    brawl, crawl, shootout, hunt, escape, zoo, ambush, safari, dig, see, short, crawlEmpty, crawlSurvival, safariSurvival, battle, battleDefense, battleSurvival, defense, defenseEmpty, screensaverRaid, screensaverBrawl, screensaverCrawl, screensaverShootout, screensaverHunt, screensaverEscape, screensaverZoo, screensaverAmbush, screensaverSafari :: ModeKind  -- What other symmetric (two only-one-moves factions) and asymmetric vs crowd -- scenarios make sense (e.g., are good for a tutorial or for standalone@@ -38,29 +89,68 @@ -- crowd ranged: no, fish in a barrel, less predictable and more fun inside --   crawl, even without reaction fire -raid = ModeKind  -- mini-crawl+raid = ModeKind   { msymbol = 'r'-  , mname   = "solo raid (1)"-  , mfreq   = [("solo", 1), ("raid", 1), ("campaign scenario", 1)]+  , mname   = "raid (tutorial, 1)"+  , mfreq   = [(RAID, 1), (CAMPAIGN_SCENARIO, 1)]+  , mtutorial = True   , mroster = rosterRaid   , mcaves  = cavesRaid-  , mendMsg = [ (Killed, "That was unfortunate. The bill for the rescue team and for the subsequent nano medbot treatment will reach the stars. Perhaps more stealth was needed? Perhaps the items lying around the area could aid survival instead of ending up ignored or passively hoarded? Or perhaps a wise course of action would be to choose a Neptune Area Administration challenge with a lower difficulty?")-              , (Defeated, "Sadly, you got worked up in the tunnels while another team snatched the prize. Remember, you are at the Outer Frontier to gain wealth and independence through industriousness and commerce and that means clashing with competing agents, not just fighting feral nature.")-              , (Escape, "You are the first to clear a route through the sewer system. Triton City authorities will now be able to establish a perimeter and mop up the side tunnels. You collect your reward of 100 gold grains and start looking for a way to invest it profitably at this Solar System's commercial frontier, abounding in more or less (usually less) regulated opportunities.\nAfter some thought you decide to start by splurging on genetic enhancement for your team. The effects won't be visible at once and you have no time to lose, having just made fresh enemies.") ]-  , mdesc   = "Neptune Area Administration confirms isolated spottings of oversize vermin in non-residential zones of the Neptune's Triton moon's largest city. To put it plainly: Triton City sewers need purging. The first person to break through to the other exit will be paid 100 gold grains. The Administration \"strongly urges participants not to resort to violence against each other.\" However, no punitive consequences are specified, not even disqualification from the contest."+  , mendMsg = [ (Killed, "That was unfortunate. The bill for the rescue team and for the subsequent nano medbot treatment will reach the stars. Perhaps more stealth was needed? Perhaps the items lying around the area could aid survival instead of ending up ignored or passively hoarded? Or perhaps a wise course of action would be to choose a Neptune Economic Area Administration challenge with a lower difficulty?")+              , (Defeated, "Sadly, you got worked up in the tunnels while another team snatched the prize. Remember, you are at the Outer Frontier to gain wealth and independence through industriousness and commerce. This pits you in a fight against competing agents, not just against the feral nature.")+              , (Escape, "You are the first to clear a route through the sewer system. Triton City authorities will now be able to establish a perimeter and mop up the side tunnels. You collect your reward of 100 gold grains and start looking for a way to invest it profitably on this Solar System's commercial frontier, abounding in more or less (usually less) regulated opportunities.\nAfter some thought you decide to start by splurging on genetic enhancement for your team. The effects won't be visible at once, but you have to plan ahead, having just made fresh enemies.") ]+  , mrules  = T.intercalate "\n"+      [ "* One level only"+      , "* Two heroes vs. Spawned enemies"+      , "* Gather gold"+      , "* Find exit and escape ASAP"+      ]+  , mdesc   = "Neptune Economic Area Administration confirms isolated spottings of oversize vermin in non-residential zones of the Neptune's Triton moon's largest city. To put it plainly: Triton City sewers need purging. The first person to break through to the other exit will be paid 100 gold grains. The Administration \"strongly urges participants not to resort to violence against each other.\" However, no punitive consequences are specified, not even disqualification from the contest."+  , mreason = "In addition to initiating the game plot, this adventure provides an introductory tutorial. Relax, explore, gather loot, find the exit and escape. With some luck, you won't even need to fight anything."+  , mhint   = "You can't use gathered items in your next encounters, so trigger any consumables at will, in particular the electronic chips as common as pebbles on the muddy sewer floors.\nFeel free to scout with only one of the heroes and keep the other one immobile, e.g., standing guard over the squad's shared inventory stash. If in grave danger, retreat with the scout to join forces with the guard. The more gold collected and the faster the victory, the higher your score in this encounter."   }  brawl = ModeKind  -- sparse melee in daylight, with shade for melee ambush   { msymbol = 'k'-  , mname   = "melee brawl (2)"-  , mfreq   = [("melee", 1), ("brawl", 1), ("campaign scenario", 1)]+  , mname   = "brawl (tutorial, 2)"+  , mfreq   = [(BRAWL, 1), (CAMPAIGN_SCENARIO, 1)]+  , mtutorial = True   , mroster = rosterBrawl   , mcaves  = cavesBrawl-  , mendMsg = [ (Killed, "That treacherous villain didn't honour his word and brought his friends to the fight. It would still not turn so bad if we remembered to use terrain to protect us from missiles or even completely hide our presence and if we honourably kept together to the end, at the same time preventing the overwhelming enemy forces from brutishly ganging up on our modest-sized, though valiant, squad. Having to repurchase genetic therapy was the most painful outcome, one that would send you broke and in shame to Earth, if repeated, to start collecting your Basic Income.")-              , (Conquer, "Bringing help was a sober and prudent move that resulted in well-earned victory and a splendid trophy of a title to a real inter-planetary space vessel. Unfortunately, the treacherous foe called reinforcements at the last moment, which start to arrive just now. It may be wise to move the celebration of the victory to a more fitting area, assuming that the dignified translocation can be accomplished timely and inconspicuously.") ]-  , mdesc   = "\"You scoundrel! You cheated in the sewers. Come alone to the woody biosphere behind the saloon at noon, if you dare. Given that I win, I take back all your gold. Otherwise, you get the scrapping rights for the giant spaceliner's hull in orbit. Yes, it's mine, you tramp; here's the docking transmitter and the paperwork. The fight is to the last man standing, no evasion, no breaks for nano-healing in town.\""+  , mendMsg = [ (Killed, "That treacherous villain didn't honour his word and brought his friends to the fight. It would still not turn so bad if we remembered to use terrain to protect us from missiles or even completely hide our presence and if we honourably kept together to the end, at the same time preventing the overwhelming enemy forces from brutishly ganging up on our modest-sized, though valiant, squad. Having to repurchase the genetic therapy is the most painful result. If repeated, that's going to send you broke and in shame to Earth, to start collecting your Basic Income.")+              , (Conquer, "Bringing help was a sober and prudent move that resulted in well-earned victory and a splendid trophy of a title to a real inter-planetary space vessel. Unfortunately, the treacherous foe called reinforcements at the last moment, a new wave arriving even now. It may be wise to move the celebration of the victory to a more fitting area, assuming that the dignified translocation can be accomplished timely and inconspicuously.") ]+  , mrules  = T.intercalate "\n"+      [ "* Two levels"+      , "* Three heroes vs. Six human enemies"+      , "* Minimize losses"+      , "* Incapacitate all enemies ASAP"+      ]+  , mdesc   = "\"You scoundrel! You cheated in the sewers, fighting two against one. Come alone to the woody biosphere behind the saloon at noon, if you dare. Given that I win, I take back all your gold. Otherwise, you get the scrapping rights for the giant spaceliner's hull in orbit.\nYes, it's mine, you tramp; here's the docking transmitter and the paperwork. The fight is to the last man standing, no evasion, no breaks for nano-healing in town.\""+  , mreason = "In addition to advancing the game plot, this encounter trains melee, squad formation, stealth and stairs use. On each level separately, the battle is symmetric, both in numbers, goals (incapacitate all enemies) and squad capabilities (only the pointman moves, while all others either melee or wait). Observe and mimic the enemies. If you can't see an enemy that apparently can see you, in reversed circumstances you would have the same advantage. Savour the relative fairness --- you won't find any in the main crawl adventure that follows."+  , mhint   = "Run a short distance with Shift or LMB, switch the pointman with Tab, repeat. In open terrain, if you keep distance between teammates, this resembles the leap frog infantry tactics. For best effects, end each sprint behind a cover or concealment.\nOnce you clear a level, descend by bumping into stairs. Use Tab to switch to remaining heroes until all gather on the new level.\nIf you get beaten repeatedly, try using all consumables you find, particularly the vials that collect healing extracts abounding in this rich biosphere. Ponder the hints from the defeat message, in particular the one about keeping your party together once the opponents are spotted. However, if you want to discover a winning tactics on your own, make sure to ignore any such tips until you succeed."   } +crawl = ModeKind+  { msymbol = 'c'+  , mname   = "long crawl (main)"+  , mfreq   = [(LONG, 1), (CRAWL, 1), (CAMPAIGN_SCENARIO, 1)]+  , mtutorial = False+  , mroster = rosterCrawl+  , mcaves  = cavesCrawl+  , mendMsg = [ (Killed, "It was not supposed to end this way. Perhaps more stealth was in order? Perhaps foes that didn't carry any key resources for your survival nor escape could have been eluded and ignored? Perhaps the gathered items should be used for survival instead of hoarded? Or perhaps the challenge, chosen freely but without awareness of the grisly difficulty, was insurmountable and lost from the very start?")+              , (Escape, "The shuttle doors close behind, docking clamps grind in farewell and the giant rotating disc slowly tumbles away in rear view. You feel at once a relief and a sense of loss. This is not finished. You are changed forever, but you know nothing. You've heard the call, but you made no answer. You came for petty change, found a treasure beyond comprehension, then barely escaped with your life as the prize.\nAnd nobody will believe you at home. But you don't need their understanding any more. You have enough money to heal, regroup, track the ship down and try again. It's your personal space cruiser, after all, with a world of its own, inside.") ]+  , mrules  = T.intercalate "\n"+      [ "* Many levels, some requiring tools to access"+      , "* Three heroes vs. Spawned enemies"+      , "* Gather gold, gems and stimpacks"+      , "* Find exit and escape ASAP"+      ]+  , mdesc   = "You are busy looting, with utmost satisfaction, the blasted bridge of an old and extravagantly luxurious cruise liner.\nSuddenly, the inert spaceship, supposedly long deserted and barely able to sustain life support, tremors and dials her fusion engines up to red overdrive. The space map flickering among the irreversibly damaged consoles shows the ship manoeuvre deftly off Triton orbit and purposefully climb the Neptune's gravity well. There's no way to control the ship and static floods all communication channels.\nYou decide to scour the nearby dilapidated decks for missing squad members, this time sending them in pairs and mapping the area properly, and then get back to the spaceport the way you came, in your shuttle. However, you are determined not to leave the ship without taking at least a portion of the wealth that is rightfully yours."+  , mreason = "This is the main, longest and most replayable scenario of the game. The fundamental goal is the survival of your crew. Sub-goals will present themselves as you take in newly visited spaceship decks and figure out ways to reach those that are presently cut off."+  , mhint   = "If you keep dying, attempt the subsequent adventures as a breather (perhaps at lowered difficulty). They fill the gaps in the plot and teach particular skills that may come in handy and help you discover new tactics of your own or come up with a strategy for staving off the attrition. On the other hand, experimenting with the initial adventures, e.g., to obtain a higher score, may open to you more efficient ways of solving the puzzles."+ -- later, when the player can visit other level sections: you turn on the deck status list display and notice that most levels are fully pressurized, including the complete autonomous slice of the disc that includes the bridge deck+  }+ -- The trajectory tip is important because of tactics of scout looking from -- behind a bush and others hiding in mist. If no suitable bushes, -- fire once and flee into mist or behind cover. Then whomever is out of LOS@@ -72,48 +162,85 @@ shootout = ModeKind  -- sparse ranged in daylight   { msymbol = 's'   , mname   = "foggy shootout (3)"-  , mfreq   = [("foggy", 1), ("shootout", 1), ("campaign scenario", 1)]+  , mfreq   = [(FOGGY, 1), (SHOOTOUT, 1), (CAMPAIGN_SCENARIO, 1)]+  , mtutorial = False   , mroster = rosterShootout   , mcaves  = cavesShootout-  , mendMsg = [ (Killed, "This is a disgrace. How is a thuggish robbery in broad daylight even possible in a moon city that styles itself as the capital of Outer System technological innovation and commercial opportunity? Where are the municipal surveillance drones, normally so eager to eavesdrop and needlessly complicate an honest tax-free business, when one's health and wealth for once depend on their nosy presence? Speaking of drones, we could use one in this skirmish, or even just a human lookout placed in a covered but unobstructed spot. Then the rest of the squad could snipe from concealment or from a safe distance.\nBarring that, we would end up in a better shape even if we all hid and fired blindly. We'd listen to impact sounds and wait with ten-fold vigilance for incoming enemy missiles, in order to register their trajectories and derive hints of enemy location. Apparently, ranged combat requires a change of pace and better planning than our previous illustrious successes accustomed us to.")+  , mendMsg = [ (Killed, "This is a disgrace. How is a thuggish robbery in broad daylight even possible in a moon city that styles itself as the capital of Outer System technological innovation and commercial opportunity? Where are the municipal surveillance drones, normally so eager to eavesdrop and needlessly complicate an honest tax-free business, when one's health and wealth for once depend on their nosy presence? Speaking of drones, we could use one in this skirmish, or even just a human lookout placed in a covered but unobstructed spot. Then the rest of the squad could snipe from concealment or from a safe distance.\nBarring that, we would end up in a better shape even if we all hid and fired blindly. We'd listen to impact sounds and wait vigilantly for incoming enemy missiles in order to register their trajectories and derive hints of enemy location. Apparently, ranged combat requires a change of pace and better planning than our previous illustrious successes accustomed us to.")               , (Conquer, "That was a good fight, with skillful application of missiles, cover and concealment. The outcome is especially commendable given the high bar of tactical proficiency. Not even professional enforcement units can routinely deduce enemy position from the trajectory of their projectiles nor by firing without line of sight and interpreting auditory cues. However, while this steep hurdle is overcome, the chase is not over yet.") ]-  , mdesc   = "The fight crashes over to a nearby mechanized farm. Law enforcement, crippled by the ban on firearms, won't show up until only wounded and dying remain to be revived and locked up. Farm supplies, scattered around, beg to be flung at foes as improvised missiles. Intense light makes it easy to aim and to discern trajectory of soaring items (point at enemy projectiles with the crosshair in aiming mode). The effects of the last infracellular therapy finally start showing."+  , mrules  = T.intercalate "\n"+      [ "* One level only"+      , "* Three heroes vs. Three human enemies"+      , "* Minimize losses"+      , "* Incapacitate all enemies ASAP"+      ]+  , mdesc   = "The fight crashes over to a nearby mechanized farm. Law enforcement, crippled by the ban on firearms, won't show up until only wounded and dying remain to be revived and locked up.\nParcels and flasks of agricultural chemicals, scattered around, beg to be flung at foes as improvised missiles. Intense light makes it easy to aim and to discern trajectory of soaring items (by pointing at enemy projectiles with the crosshair in aiming mode). The effects of your last infracellular therapy finally start showing."+  , mreason = "This adventure is a flashback, picking the plot up where brawl (2) left it. It also teaches specifically the ranged combat skill in the simplified setup of a fully symmetric battle."+  , mhint   = "Try to come up with the best squad formation for this tactical challenge. Don't despair if you run out of ammo, because if you aim truly, enemy is left with few hit points remaining. Fight, ranged or melee, until all aggressors are disabled."   }  hunt = ModeKind  -- melee vs ranged with reaction fire in daylight   { msymbol = 'h'   , mname   = "perilous hunt (4)"-  , mfreq   = [("perilous", 1), ("hunt", 1), ("campaign scenario", 1)]+  , mfreq   = [(PERILOUS, 1), (HUNT, 1), (CAMPAIGN_SCENARIO, 1)]+  , mtutorial = False   , mroster = rosterHunt   , mcaves  = cavesHunt-  , mendMsg = [ (Killed, "Next time let's try to remember we are not on a sightseeing expedition. Also, leaving concealment is risky, leaving cover is foolhardy and wandering off is deadly. Also, what was that taking pictures by the mangrove tree all about? Were you trying to immortalize your handsome faces in case our shared pool of money was not enough to revive your sorry carcasses after the defeat? Good call, because after paying the techno-medical bills we are broke and the military grade communication and reaction fire implants of the gang foot soldiers that chase us don't raise our prospects either. And we were so close to complete victory and unfathomable wealth, if only we strove to lower the difficulty of this mission instead of raising it.")+  , mendMsg = [ (Killed, "Next time let's try to remember we are not on a sightseeing expedition. Also, leaving concealment is risky, leaving cover is foolhardy and wandering off is deadly. Also, what was that taking pictures by the mangrove tree all about? Were you trying to immortalize your handsome faces in case our shared pool of money was not enough to revive your sorry carcasses after the defeat? Good call, because after paying the techno-medical bills we are broke, while the gang foot soldiers that chase us seem to have military grade communication and reaction fire implants. And we were so close to complete victory and unfathomable wealth, if only we strove to lower the difficulty of this mission instead of raising it.")       -- the guy is wrong about implants (though the items are genetically attuned), but being wrong is plausible when the team is killed off/chased off and can't scour the battleground       -- this is in the middle of the scenario list and the mission is not tricky, so a subtle reminder about lowering difficulty, in case the player struggles               , (Conquer, "We chased them off, like we knew that we would. It feels nice to stick together and prevail. Now we can do no wrong just minding our business and going our way to the spaceport. We taught them a lesson, despite their superior equipment, and nobody else needs to be harmed while we take possession of our rightful property, the glorious spaceship in Triton's orbit.") ]-  , mdesc   = "Who is the hunter and who is the prey? The only criterion is remaining conscious versus becoming incapacitated when the chase ends."+  , mrules  = T.intercalate "\n"+      [ "* One level only"+      , "* Seven heroes vs. Seven human enemies capable of concurrent attacks"+      , "* Minimize losses"+      , "* Incapacitate all enemies ASAP"+      ]+  , mdesc   = "Who is the hunter and who is the prey? The only criterion is last man standing when the chase ends."+  , mreason = "This is yet another reminiscence of the events that led to the long crawl adventure. This encounter is quite a tactical challenge, because enemies are allowed to fling their ammo simultaneously at your team, which has no such ability."+  , mhint   = "Try not to outshoot the enemy, but to instead focus more on melee tactics. A useful concept here is communication overhead. Any team member that is not waiting and spotting for everybody, but acts, e.g., melees or moves or manages items, slows down all other team members by roughly 10%, because they need to keep track of his actions. Therefore, if other heroes melee, consider carefully if it makes sense to come to their aid, slowing them while you move, or if it's better to stay put and monitor the perimeter. This is true for all factions and all actors on each level separately, except the pointman of each faction, if it has one."  -- this also eliminates lag in big battles and helps the player to focus on combat and not get distracted by distant team members frantically trying to reach the battleground in time   }  escape = ModeKind  -- asymmetric ranged and stealth race at night   { msymbol = 'e'   , mname   = "night escape (5)"-  , mfreq   = [("night", 1), ("escape", 1), ("campaign scenario", 1)]+  , mfreq   = [(NIGHT, 1), (ESCAPE, 1), (CAMPAIGN_SCENARIO, 1)]+  , mtutorial = False   , mroster = rosterEscape   , mcaves  = cavesEscape-  , mendMsg = [ (Killed, "Somebody must have tipped the gang guards off. However, us walking along a lit trail, yelling, could have been a contributing factor. Also, it's worth noting that the torches prepared for this assault are best used as thrown makeshift flares. On the other hand, equipping a lit torch makes one visible in the dark, regrettably but not quite unexpectedly. Lastly, the goal of this foray was to find the exit back to the city, marked by a yellow '>' sign, and to gather some treasure along the way, but not to bedevil every local evildoer, as much as they do deserve it.")+  , mendMsg = [ (Killed, "Somebody must have tipped the gang guards off. However, us walking along a lit trail, yelling, could have been a contributing factor. Also, it's worth noting that the torches prepared for this assault are best used as thrown makeshift flares. On the other hand, equipping a lit torch makes one visible in the dark, regrettably but not quite unexpectedly. Lastly, the goal of this foray was to find the exit back to the city, marked by a yellow '>' sign, and to gather some treasure along the way. Not to harass every local evildoer, as much as they do deserve it.")               , (Conquer, "It was enough to reach the escape area, namely the exit tunnel from the park marked by yellow '>' symbol. Spilling that much blood was risky and unnecessary. Having said that --- impressive indeed.")               , (Escape, "Congratulations, you took your revenge and it's heavy in your pockets.") ]+  , mrules  = T.intercalate "\n"+      [ "* One level only"+      , "* Three heroes vs. Seven human enemies capable of concurrent attacks"+      , "* Minimize losses"+      , "* Gather gems"+      , "* Find exit and escape ASAP"+      ]   , mdesc   = "Bloodied spaceship deed in hand notwithstanding, you can reach the derelict spaceliner only via a shuttle from the Central Triton Spaceport across the city. After hours of being chased in the opposite direction towards the border wall, you sneak back and make a desperate dash through the very den of the pursuing gang. Any valuables you come upon in this public park turned miscreant lair will be fair compensation for your losses, but you need to find the exit before the foes find you. Rein in your wrath and don't attack your tormentors. Foiling their plans by eluding them will be revenge enough."+  , mreason = "The focus of this installment is on stealthy exploration under the threat of numerically superior enemy."+  , mhint   = ""   }  zoo = ModeKind  -- asymmetric crowd melee at night   { msymbol = 'b'   , mname   = "burning zoo (6)"-  , mfreq   = [("burning", 1), ("zoo", 1), ("campaign scenario", 1)]+  , mfreq   = [(BURNING, 1), (ZOO, 1), (CAMPAIGN_SCENARIO, 1)]+  , mtutorial = False   , mroster = rosterZoo   , mcaves  = cavesZoo   , mendMsg = [ (Killed, "Against such an onslaught, only clever positioning, use of terrain and patient vigilance gives any chance of survival.")               , (Conquer, "That was a grim harvest. The city is safe again. So are your precious selves, with nothing and no one blocking your way to the spaceport any more.") ]-  , mdesc   = "As justified and satisfying as setting the enemy headquarters on fire has been, it backfires when the blaze immediately spreads to the public zoo on the path to the spaceport. Crazed animals mill around while the flames ignite greenery and consume nets, cages and security equipment. Whether that's a good sign or bad, apparently nobody is willing to pursue you any more. You are on your own, having to completely clean up the area, up to the last lurking predator, in order to safely move through."+  , mrules  = T.intercalate "\n"+      [ "* One level only"+      , "* Five heroes vs. Many enemies"+      , "* Minimize losses"+      , "* Incapacitate all enemies ASAP"+      ]+  , mdesc   = "As justified and satisfying as the setting of enemy headquarters on fire has been, it backfires when the blaze immediately spreads to the public zoo on the path to the spaceport. Crazed animals mill around while the flames ignite greenery and consume nets, cages and security equipment. Whether that's a good sign or bad, apparently nobody is willing to pursue you any more. You are on your own, having to completely clean up the area, up to the last lurking predator, in order to safely move through."+  , mreason = "This is a crowd control exercise, at night, with a raging fire."+  , mhint   = "Note that communication overhead, as explained in perilous hunt adventure hints, makes it impossible for any faction to hit your heroes by more than 10 normal speed actors each turn. However, this is still too many, so position is everything."   }  -- The tactic is to sneak in the dark, highlight enemy with thrown torches@@ -127,34 +254,40 @@ ambush = ModeKind  -- dense ranged with reaction fire vs melee at night   { msymbol = 'm'   , mname   = "ranged ambush (7)"-  , mfreq   = [("ranged", 1), ("ambush", 1), ("campaign scenario", 1)]+  , mfreq   = [(RANGED, 1), (AMBUSH, 1), (CAMPAIGN_SCENARIO, 1)]+  , mtutorial = False   , mroster = rosterAmbush   , mcaves  = cavesAmbush-  , mendMsg = [ (Killed, "You turned out to be the prey, this time, not the hunter. In fact, you are not even in the hunters' league. And it's hard to operate a space dock while being chased round a spaceport. When fighting against such odds, passively waiting for enemy to spring a trap is to no avail, because a professional team can sneak in darkness and ambush the ambushers. Granted, good positioning is crucial, so that each squad member can overwatch the battlefield and fire opportunistically, using the recently recovered mil-grade communication equipment. However, there is no hope without active scouting, throwing lit objects and probing suspect areas with missiles while paying attention to sounds. And that may still not be enough.")-              , (Conquer, "The new communication equipment, enabling simultaneous ranged attacks with indirect aiming, apparently proved effective beyond expectations. With the mercenaries gone and nobody else having the slightest wish to interfere, the shuttle to the space cruiser at orbit is easy to launch at last. Now is your moment of glory. Now your turbulent adventure ends and the boring life of space cruiser scrap parts supplier or, as it may be, of a refurbished giant space liner operator, commences.\nA pity that the last round of shoddy genetic enhancements, bought at the grey market, scandalously auto-reverts at this very moment, leaving the personalized equipment that attuned to the previous genetic configuration inoperable. Fortunately, danger, debt and the gangster debt collectors are now behind you and the grey market won't see you again.") ]-  , mdesc   = "Not even the unexplained carnage at the Central Triton Spaceport will prevent you from claiming the prize awaiting you at the orbit. After all, you didn't take to the stars to let others decide your fate. There is still no news coverage from the ruin of what was the largest and tightest security facility in the whole Neptune Area. Without waiting for explanations nor for the personnel to return, you creep along the burning booths, scouting for any airlock with a shuttle still attached."  -- this is the only scenario with no objective specified, to give a bit of suspense, misdirection and mystery until the first win (death gives a hint only); being the last of the small scenarios, it won't scare off new players-  }--crawl = ModeKind-  { msymbol = 'c'-  , mname   = "upward crawl (long)"-  , mfreq   = [("upward", 1), ("crawl", 1), ("campaign scenario", 1)]-  , mroster = rosterCrawl-  , mcaves  = cavesCrawl-  , mendMsg = [ (Killed, "It was not supposed to end this way. Perhaps more stealth was in order? Perhaps the gathered items should be used for survival instead of hoarded? Or perhaps the challenge, chosen freely but without awareness of the grisly difficulty, was insurmountable and lost from the very start? Nobody is going to find out, even if humans ever set their feet here again and prevail, another time, another way.")-              , (Escape, "The shuttle doors close behind, docking clamps grind in farewell and the giant rotating disc slowly tumbles away in rear view. You feel at once a relief and a sense of loss. This is not finished. You are changed forever, but you know nothing. You've heard the call, but you made no answer. You came for petty change, found a treasure beyond comprehension, then barely escaped with your life as the prize.\nAnd nobody will believe you at home. But you don't need their understanding any more. You have enough money to heal, regroup, track the ship down and try again. It's your personal space cruiser, after all, with a world of its own, inside.") ]-  , mdesc   = "You are busy looting, with utmost satisfaction, the blasted bridge of an old and extravagantly luxurious cruise liner. Suddenly, the inert spaceship, supposedly long deserted and barely able to sustain life support, tremors and dials her fusion engines up to red overdrive. The space map flickering among the irreversibly damaged consoles shows the ship manoeuvre deftly off Triton orbit and purposefully climb the Neptune's gravity well. There's no way to control the ship and static floods all communication channels. You decide to scour the nearby dilapidated decks for missing squad members and get back to the spaceport the way you came, in your shuttle. However, you are determined not to leave the ship without taking at least a portion of the wealth that is rightfully yours."- -- later, when the player can visit other level sections: you turn on the deck status list display and notice that most levels are fully pressurized, including the complete autonomous slice of the disc that includes the bridge deck+  , mendMsg = [ (Killed, "You turned out to be the prey, this time, not the hunter. In fact, you are not even in the hunters' league. When fighting against such odds, passively waiting for enemy to spring a trap is to no avail, because a professional team can sneak in darkness and ambush the ambushers. Granted, good positioning is crucial, so that each squad member can overwatch the battlefield and fire opportunistically, using the recently recovered mil-grade communication equipment. However, there is no hope without active scouting, throwing lit objects and probing suspect areas with missiles while paying attention to sounds. And that may still not be enough.")+              , (Conquer, "The new communication equipment enabling simultaneous ranged attacks with indirect aiming proved effective beyond expectation. With the mercenaries gone and nobody else having the slightest wish to interfere, the shuttle to the space cruiser at orbit is easy to launch at last. You let yourself bask in the battle's afterglow of bliss and relief. Now your turbulent adventure ends and the boring life of space cruiser scrap parts supplier or, as it may be, of a refurbished giant space liner operator, commences.\nA pity that the last round of shoddy genetic enhancements, bought at the grey market, scandalously auto-reverts at this very moment, leaving your personalized equipment that attuned to the previous genetic configuration inoperable. Fortunately, danger, debt and the gangster debt collectors are now behind you and the grey market won't see you ever again.") ]+  , mrules  = T.intercalate "\n"+      [ "* One level only"+      , "* Three heroes with concurrent attacks vs. Unidentified foes"+      , "* Minimize losses"+      , "* Assert control of the situation ASAP"+      ]+  , mdesc   = "Not even the unexplained carnage at the Central Triton Spaceport will prevent you from claiming the prize awaiting at the orbit. After all, you didn't take to the stars to let others decide your fate. There is still no news coverage from this ruin of what was the largest facility with tightest security in the whole Neptune Economic Area. Without waiting for explanations nor for the personnel to return, you creep along the burning booths, scouting for any airlock with a shuttle still attached and a way to restore power needed for the docking gear."+  , mreason = "In this encounter, finally, your heroes are able to all use ranged attacks simultaneously, given enough ammunition. Once you prevail in the encounter, the story catches up with the start of the main adventure, the long crawl." -- this is the only scenario with no objective specified, to give a bit of suspense, misdirection and mystery until the first win (death gives a hint only); being the last of the small scenarios, it won't scare off new players+  , mhint   = "Beware of friendly fire, particularly from explosives. But you need no more hints. Go fulfill your destiny!"   }  safari = ModeKind  -- Easter egg available only via screensaver   { msymbol = 'f'   , mname   = "safari"-  , mfreq   = [("safari", 1)]+  , mfreq   = [(SAFARI, 1)]+  , mtutorial = False   , mroster = rosterSafari   , mcaves  = cavesSafari   , mendMsg = []+  , mrules  = T.intercalate "\n"+      [ "* Three levels"+      , "* Many teammates capable of concurrent action vs. Many enemies"+      , "* Minimize losses"+      , "* Find exit and escape ASAP"+      ]   , mdesc   = "\"In this simulation you'll discover the joys of hunting the most exquisite of Earth's flora and fauna, both animal and semi-intelligent. Exit at the topmost level.\" This is a VR recording recovered from an alien nest debris."+  , mreason = "This is an Easter egg. The default squad doctrine is that all team members follow the pointman, but it can be changed from the settings submenu of the main menu."+  , mhint   = ""   }  -- * Testing modes@@ -162,180 +295,227 @@ dig = ModeKind   { msymbol = 'd'   , mname   = "dig"-  , mfreq   = [("dig", 1)]+  , mfreq   = [(DIG, 1)]+  , mtutorial = False   , mroster = rosterCrawlEmpty   , mcaves  = cavesDig   , mendMsg = []+  , mrules  = ""   , mdesc   = "Delve deeper!"+  , mreason = ""+  , mhint   = ""   }  see = ModeKind   { msymbol = 'a'   , mname   = "see"-  , mfreq   = [("see", 1)]+  , mfreq   = [(SEE, 1)]+  , mtutorial = False   , mroster = rosterCrawlEmpty   , mcaves  = cavesSee   , mendMsg = []+  , mrules  = ""   , mdesc   = "See all!"+  , mreason = ""+  , mhint   = ""   } +short = ModeKind+  { msymbol = 's'+  , mname   = "short"+  , mfreq   = [(SHORT, 1)]+  , mtutorial = False+  , mroster = rosterCrawlEmpty+  , mcaves  = cavesShort+  , mendMsg = []+  , mrules  = ""+  , mdesc   = "See all short scenarios!"+  , mreason = ""+  , mhint   = ""+  }+ crawlEmpty = ModeKind   { msymbol = 'c'   , mname   = "crawl empty"-  , mfreq   = [("crawl empty", 1)]+  , mfreq   = [(CRAWL_EMPTY, 1)]+  , mtutorial = False   , mroster = rosterCrawlEmpty   , mcaves  = cavesCrawlEmpty   , mendMsg = []-  , mdesc   = "Enjoy the free space."+  , mrules  = ""+  , mdesc   = "Enjoy the extra legroom."+  , mreason = ""+  , mhint   = ""   }  crawlSurvival = ModeKind   { msymbol = 'd'   , mname   = "crawl survival"-  , mfreq   = [("crawl survival", 1)]+  , mfreq   = [(CRAWL_SURVIVAL, 1)]+  , mtutorial = False   , mroster = rosterCrawlSurvival   , mcaves  = cavesCrawl   , mendMsg = []+  , mrules  = ""   , mdesc   = "Lure the human intruders deeper and deeper."+  , mreason = ""+  , mhint   = ""   }  safariSurvival = ModeKind   { msymbol = 'u'   , mname   = "safari survival"-  , mfreq   = [("safari survival", 1)]+  , mfreq   = [(SAFARI_SURVIVAL, 1)]+  , mtutorial = False   , mroster = rosterSafariSurvival   , mcaves  = cavesSafari   , mendMsg = []+  , mrules  = ""   , mdesc   = "In this simulation you'll discover the joys of being hunted among the most exquisite of Earth's flora and fauna, both animal and semi-intelligent."+  , mreason = ""+  , mhint   = ""   }  battle = ModeKind   { msymbol = 'b'   , mname   = "battle"-  , mfreq   = [("battle", 1)]+  , mfreq   = [(BATTLE, 1)]+  , mtutorial = False   , mroster = rosterBattle   , mcaves  = cavesBattle   , mendMsg = []-  , mdesc   = "Odds are stacked against those that unleash the horrors of abstraction."+  , mrules  = ""+  , mdesc   = "Odds are stacked against those that reveal what should have been kept in the dark."+  , mreason = ""+  , mhint   = ""   }  battleDefense = ModeKind   { msymbol = 'f'   , mname   = "battle defense"-  , mfreq   = [("battle defense", 1)]+  , mfreq   = [(BATTLE_DEFENSE, 1)]+  , mtutorial = False   , mroster = rosterBattleDefense   , mcaves  = cavesBattle   , mendMsg = []-  , mdesc   = "Odds are stacked for those that breathe mathematics."+  , mrules  = ""+  , mdesc   = "Odds are stacked for those that set the rules."+  , mreason = ""+  , mhint   = ""   }  battleSurvival = ModeKind   { msymbol = 'i'   , mname   = "battle survival"-  , mfreq   = [("battle survival", 1)]+  , mfreq   = [(BATTLE_SURVIVAL, 1)]+  , mtutorial = False   , mroster = rosterBattleSurvival   , mcaves  = cavesBattle   , mendMsg = []+  , mrules  = ""   , mdesc   = "Odds are stacked for those that ally with the strongest."+  , mreason = ""+  , mhint   = ""   }  defense = ModeKind  -- perhaps a real scenario in the future   { msymbol = 'e'   , mname   = "defense"-  , mfreq   = [("defense", 1)]+  , mfreq   = [(DEFENSE, 1)]+  , mtutorial = False   , mroster = rosterDefense   , mcaves  = cavesCrawl   , mendMsg = []+  , mrules  = ""   , mdesc   = "Don't let the half-witted humans derail your operation and flee, like the puny, naked, tentacle-less beasts that they are!"+  , mreason = "This is an initial sketch of the reversed crawl game mode. Play on high difficulty to avoid guaranteed victories against the pitiful humans."+  , mhint   = ""   }  defenseEmpty = ModeKind   { msymbol = 'e'   , mname   = "defense empty"-  , mfreq   = [("defense empty", 1)]+  , mfreq   = [(DEFENSE_EMPTY, 1)]+  , mtutorial = False   , mroster = rosterDefenseEmpty   , mcaves  = cavesCrawlEmpty   , mendMsg = []-  , mdesc   = "Lord over."+  , mrules  = ""+  , mdesc   = "Lord over empty halls."+  , mreason = ""+  , mhint   = ""   }  -- * Screensaver modes -screensave :: AutoLeader -> Roster -> Roster-screensave auto r =-  let f [] = []-      f ((player, initial) : rest) =-        (player {fleaderMode = LeaderAI auto}, initial) : rest-  in r {rosterList = f $ rosterList r}--screensaverRaid = raid+screensaverRaid = screensave (AutoLeader False False) $ raid   { mname   = "auto-raid (1)"-  , mfreq   = [("insert coin", 1), ("no confirms", 1)]-  , mroster = screensave (AutoLeader False False) rosterRaid+  , mfreq   = [(INSERT_COIN, 2), (NO_CONFIRMS, 1)]   } -screensaverBrawl = brawl+screensaverBrawl = screensave (AutoLeader False False) $ brawl   { mname   = "auto-brawl (2)"-  , mfreq   = [("no confirms", 1)]-  , mroster = screensave (AutoLeader False False) rosterBrawl+  , mfreq   = [(NO_CONFIRMS, 1)]   } -screensaverShootout = shootout+screensaverCrawl = screensave (AutoLeader False False) $ crawl+  { mname   = "auto-crawl (long)"+  , mfreq   = [(NO_CONFIRMS, 1)]+  }++screensaverShootout = screensave (AutoLeader False False) $ shootout   { mname   = "auto-shootout (3)"-  , mfreq   = [("insert coin", 1), ("no confirms", 1)]-  , mroster = screensave (AutoLeader False False) rosterShootout+  , mfreq   = [(INSERT_COIN, 2), (NO_CONFIRMS, 1)]   } -screensaverHunt = hunt+screensaverHunt = screensave (AutoLeader False False) $ hunt   { mname   = "auto-hunt (4)"-  , mfreq   = [("insert coin", 1), ("no confirms", 1)]-  , mroster = screensave (AutoLeader False False) rosterHunt+  , mfreq   = [(INSERT_COIN, 2), (NO_CONFIRMS, 1)]   } -screensaverEscape = escape+screensaverEscape = screensave (AutoLeader False False) $ escape   { mname   = "auto-escape (5)"-  , mfreq   = [("insert coin", 1), ("no confirms", 1)]-  , mroster = screensave (AutoLeader False False) rosterEscape+  , mfreq   = [(INSERT_COIN, 2), (NO_CONFIRMS, 1)]   } -screensaverZoo = zoo+screensaverZoo = screensave (AutoLeader False False) $ zoo   { mname   = "auto-zoo (6)"-  , mfreq   = [("no confirms", 1)]-  , mroster = screensave (AutoLeader False False) rosterZoo+  , mfreq   = [(NO_CONFIRMS, 1)]   } -screensaverAmbush = ambush+screensaverAmbush = screensave (AutoLeader False False) $ ambush   { mname   = "auto-ambush (7)"-  , mfreq   = [("no confirms", 1)]-  , mroster = screensave (AutoLeader False False) rosterAmbush-  }--screensaverCrawl = crawl-  { mname   = "auto-crawl (long)"-  , mfreq   = [("no confirms", 1)]-  , mroster = screensave (AutoLeader False False) rosterCrawl+  , mfreq   = [(NO_CONFIRMS, 1)]   } -screensaverSafari = safari+-- changing leader by client needed, because of TFollow+screensaverSafari = screensave (AutoLeader False True) $ safari   { mname   = "auto-safari"-  , mfreq   = [("insert coin", 1), ("no confirms", 1)]-  , mroster = -- changing leader by client needed, because of TFollow-              screensave (AutoLeader False True) rosterSafari+  , mfreq   = [(INSERT_COIN, 1), (NO_CONFIRMS, 1)]   } -rosterRaid, rosterBrawl, rosterShootout, rosterHunt, rosterEscape, rosterZoo, rosterAmbush, rosterCrawl, rosterCrawlEmpty, rosterCrawlSurvival, rosterSafari, rosterSafariSurvival, rosterBattle, rosterBattleDefense, rosterBattleSurvival, rosterDefense, rosterDefenseEmpty :: Roster+teamCompetitor, teamCivilian, teamMercenary :: TeamContinuity+teamCompetitor = TeamContinuity 2+teamCivilian = TeamContinuity 3+teamMercenary = TeamContinuity 4 +rosterRaid, rosterBrawl, rosterCrawl, rosterShootout, rosterHunt, rosterEscape, rosterZoo, rosterAmbush, rosterSafari, rosterCrawlEmpty, rosterCrawlSurvival, rosterSafariSurvival, rosterBattle, rosterBattleDefense, rosterBattleSurvival, rosterDefense, rosterDefenseEmpty :: Roster+ rosterRaid = Roster   { rosterList = [ ( playerHero {fhiCondPoly = hiHeroShort}-                   , [(3, 1, "hero")] )+                   , Just teamExplorer+                   , [(2, 2, HERO)] )                  , ( playerAntiHero { fname = "Red Collar Bro"                                     , fhiCondPoly = hiHeroShort }-                   , [(3, 1, "hero")] )+                   , Just teamCompetitor+                   , [(2, 1, RANGER_HERO)] )                  , ( playerAnimal  -- starting over escape-                   , [(3, 2, "animal")] )+                   , Nothing+                   , [(2, 2, ANIMAL)] )                  , ( playerRobot-                   , [(3, 1, "robot")] )-                 , (playerHorror, []) ]  -- for summoned monsters+                   , Nothing+                   , [(2, 1, ROBOT)] )+                 , (playerHorror, Nothing, []) ]  -- for summoned monsters   , rosterEnemy = [ ("Spacefarer", "Animal Kingdom")                   , ("Spacefarer", "Robot Anarchy")                   , ("Spacefarer", "Horror Den")@@ -348,17 +528,44 @@ rosterBrawl = Roster   { rosterList = [ ( playerHero { fcanEscape = False                                 , fhiCondPoly = hiHeroMedium }-                   , [(4, 3, "hero")] )+                   , Just teamExplorer+                   , [(3, 3, BRAWLER_HERO)] )+                       -- start heroes on stairs, since they go first                  , ( playerAntiHero { fname = "Red Collar Bro"                                     , fcanEscape = False                                     , fhiCondPoly = hiHeroMedium }-                   , [(4, 3, "hero")] )-                 , (playerHorror, []) ]+                   , Just teamCompetitor+                   , [ (3, 3, BRAWLER_HERO)+                     , (2, 3, BRAWLER_HERO) ] )+                 , (playerHorror, Nothing, []) ]   , rosterEnemy = [ ("Spacefarer", "Red Collar Bro")                   , ("Spacefarer", "Horror Den")                   , ("Red Collar Bro", "Horror Den") ]   , rosterAlly = [] } +rosterCrawl = Roster+  { rosterList = [ ( playerHero+                   , Just teamExplorer+                   , [(3, 3, CRAWL_HERO)] )+                 , ( playerMonster+                   , Nothing+                   , [] )+                 , ( playerAnimal+                   , Nothing+                   , [ (2, 5, ANIMAL)+                     , (3, 4, ANIMAL)+                     , -- Optional huge battle at the end:+                       (15, 100, MOBILE_ANIMAL) ] )+                 , ( playerRobot+                   , Nothing+                   , [(2, 4, ROBOT)] ) ]+  , rosterEnemy = [ ("Spacefarer", "Alien Hierarchy")+                  , ("Spacefarer", "Animal Kingdom")+                  , ("Spacefarer", "Robot Anarchy") ]+  , rosterAlly = [ ("Alien Hierarchy", "Animal Kingdom")+                 , ("Alien Hierarchy", "Robot Anarchy")+                 , ("Robot Anarchy", "Animal Kingdom") ] }+ -- Exactly one scout gets a sight boost, to help the aggressor, because he uses -- the scout for initial attack, while camper (on big enough maps) -- can't guess where the attack would come and so can't position his single@@ -366,12 +573,14 @@ rosterShootout = Roster   { rosterList = [ ( playerHero { fcanEscape = False                                 , fhiCondPoly = hiHeroMedium }-                   , [(5, 1, "scout hero"), (5, 2, "ranger hero")] )+                   , Just teamExplorer+                   , [(5, 2, RANGER_HERO), (5, 1, SCOUT_HERO)] )                  , ( playerAntiHero { fname = "Red Collar Bro"                                     , fcanEscape = False                                     , fhiCondPoly = hiHeroMedium }-                   , [(5, 1, "scout hero"), (5, 2, "ranger hero")] )-                 , (playerHorror, []) ]+                   , Just teamCompetitor+                   , [(5, 2, RANGER_HERO), (5, 1, SCOUT_HERO)] )+                 , (playerHorror, Nothing, []) ]   , rosterEnemy = [ ("Spacefarer", "Red Collar Bro")                   , ("Spacefarer", "Horror Den")                   , ("Red Collar Bro", "Horror Den") ]@@ -380,26 +589,31 @@ rosterHunt = Roster   { rosterList = [ ( playerHero { fcanEscape = False                                 , fhiCondPoly = hiHeroMedium }-                   , [(7, 7, "soldier hero")] )+                   , Just teamExplorer+                   , [(6, 7, SOLDIER_HERO)] )                  , ( playerAntiHero { fname = "Red Collar Bro"                                     , fcanEscape = False                                     , fhiCondPoly = hiHeroMedium }-                   , [(7, 1, "scout hero"), (7, 6, "ambusher hero")] )-                 , (playerHorror, []) ]+                   , Just teamCompetitor+                   , [(6, 6, AMBUSHER_HERO), (6, 1, SCOUT_HERO)] )+                 , (playerHorror, Nothing, []) ]   , rosterEnemy = [ ("Spacefarer", "Red Collar Bro")                   , ("Spacefarer", "Horror Den")                   , ("Red Collar Bro", "Horror Den") ]   , rosterAlly = [] }  rosterEscape = Roster-  { rosterList = [ ( playerHero {fhiCondPoly = hiHeroMedium}-                   , [(9, 1, "scout hero"), (9, 2, "escapist hero")] )-                 , ( playerAntiHero { fname = "Red Collar Bro"+  { rosterList = [ ( playerAntiHero { fname = "Red Collar Bro"                                     , fcanEscape = False  -- start on escape                                     , fneverEmpty = False  -- loot after killing                                     , fhiCondPoly = hiHeroMedium }-                   , [(9, 1, "scout hero"), (9, 6, "ambusher hero")] )-                 , (playerHorror, []) ]+                   , Just teamCompetitor+                   , [(7, 6, AMBUSHER_HERO), (7, 1, SCOUT_HERO)] )+                 , ( playerHero {fhiCondPoly = hiHeroMedium}+                   , Just teamExplorer+                   , [(7, 2, ESCAPIST_HERO), (7, 1, SCOUT_HERO)] )+                     -- second on the list to let bros occupy the exit+                 , (playerHorror, Nothing, []) ]   , rosterEnemy = [ ("Spacefarer", "Red Collar Bro")                   , ("Spacefarer", "Horror Den")                   , ("Red Collar Bro", "Horror Den") ]@@ -408,10 +622,12 @@ rosterZoo = Roster   { rosterList = [ ( playerHero { fcanEscape = False                                 , fhiCondPoly = hiHeroLong }-                   , [(11, 5, "soldier hero")] )+                   , Just teamExplorer+                   , [(8, 5, SOLDIER_HERO)] )                  , ( playerAnimal {fneverEmpty = True}-                   , [(11, 100, "mobile animal")] )-                 , (playerHorror, []) ]  -- for summoned monsters+                   , Nothing+                   , [(8, 100, MOBILE_ANIMAL)] )+                 , (playerHorror, Nothing, []) ]  -- for summoned monsters   , rosterEnemy = [ ("Spacefarer", "Animal Kingdom")                   , ("Spacefarer", "Horror Den") ]   , rosterAlly = [] }@@ -419,65 +635,35 @@ rosterAmbush = Roster   { rosterList = [ ( playerHero { fcanEscape = False                                 , fhiCondPoly = hiHeroMedium }-                   , [(13, 1, "scout hero"), (13, 5, "ambusher hero")] )+                   , Just teamExplorer+                   , [(9, 5, AMBUSHER_HERO), (9, 1, SCOUT_HERO)] )                  , ( playerAntiHero { fname = "Gray Off-World Mercenary"                                     , fcanEscape = False                                     , fhiCondPoly = hiHeroMedium }-                   , [(13, 12, "soldier hero")] )-                 , (playerHorror, []) ]+                   , Just teamMercenary+                   , [(9, 12, MERCENARY_HERO)] )+                 , (playerHorror, Nothing, []) ]   , rosterEnemy = [ ("Spacefarer", "Gray Off-World Mercenary")                   , ("Spacefarer", "Horror Den")                   , ("Gray Off-World Mercenary", "Horror Den") ]   , rosterAlly = [] } -rosterCrawl = Roster-  { rosterList = [ ( playerHero-                   , [(3, 3, "crawl hero")] )-                 , ( playerMonster-                   , [] )-                 , ( playerAnimal-                   , -- Optional huge battle at the end:-                     [(15, 100, "mobile animal")] )-                 , ( playerRobot-                   , [] ) ]  -- gentle introduction-  , rosterEnemy = [ ("Spacefarer", "Alien Hierarchy")-                  , ("Spacefarer", "Animal Kingdom")-                  , ("Spacefarer", "Robot Anarchy") ]-  , rosterAlly = [ ("Alien Hierarchy", "Animal Kingdom")-                 , ("Alien Hierarchy", "Robot Anarchy")-                 , ("Robot Anarchy", "Animal Kingdom") ] }--rosterCrawlEmpty = Roster-  { rosterList = [ ( playerHero-                   , [(1, 1, "crawl hero")] )-                 , (playerHorror, []) ]  -- for spawned and summoned monsters-  , rosterEnemy = []-  , rosterAlly = [] }--rosterCrawlSurvival = rosterCrawl-  { rosterList = [ ( playerAntiHero-                   , [(3, 3, "crawl hero")] )-                 , ( playerMonster-                   , [] )-                 , ( playerAnimal {fhasUI = True}-                   , -- Fun from the start to avoid empty initial level:-                     [ (3, 5 + 1 `d` 2, "animal")  -- many, because no spawning-                     -- Optional huge battle at the end:-                     , (15, 100, "mobile animal") ] )-                 , ( playerRobot-                   , [] ) ] }  -- gentle introduction- -- No horrors faction needed, because spawned heroes land in civilian faction. rosterSafari = Roster   { rosterList = [ ( playerMonsterTourist-                   , [(5, 15, "monster")] )+                   , Nothing+                   , [(5, 15, MONSTER)] )                  , ( playerHunamConvict-                   , [(5, 2, "civilian")] )+                   , Just teamCivilian+                   , [(5, 2, CIVILIAN)] )                  , ( playerAnimalMagnificent-                   , [(10, 15, "mobile animal")] )+                   , Nothing+                   , [(10, 15, MOBILE_ANIMAL)] )                  , ( playerAnimalExquisite  -- start on escape-                   , [(15, 20, "mobile animal")] )-                 , (playerHorror, []) ]  -- construction hooter; neutral+                   , Nothing+                   , [(15, 20, MOBILE_ANIMAL)] )+                 , (playerHorror, Nothing, []) ]+                     -- construction hooter; neutral   , rosterEnemy = [ ("Alien Tourist Office", "Hunam Convict")                   , ( "Alien Tourist Office"                     , "Animal Magnificent Specimen Variety" )@@ -490,31 +676,62 @@   , rosterAlly = [ ( "Animal Magnificent Specimen Variety"                    , "Animal Exquisite Herds and Packs Galore" ) ] } +rosterCrawlEmpty = Roster+  { rosterList = [ ( playerHero+                   , Just teamExplorer+                   , [(1, 1, CRAWL_HERO)] )+                 , (playerHorror, Nothing, []) ]+                     -- for spawned and summoned monsters+  , rosterEnemy = []+  , rosterAlly = [] }++rosterCrawlSurvival = rosterCrawl+  { rosterList = [ ( playerAntiHero+                   , Just teamExplorer+                   , [(3, 3, CRAWL_HERO)] )+                 , ( playerMonster+                   , Nothing+                   , [(5, 1, MONSTER)] )+                 , ( playerAnimal {fhasUI = True}+                   , Nothing+                   , [(5, 10, ANIMAL)] )  -- explore unopposed for some time+                 , ( playerRobot+                   , Nothing+                   , [(3, 3, ROBOT)] ) ] }+ rosterSafariSurvival = rosterSafari   { rosterList = [ ( playerMonsterTourist                        { fleaderMode = LeaderAI $ AutoLeader True True                        , fhasUI = False }-                   , [(5, 15, "monster")] )+                   , Nothing+                   , [(5, 15, MONSTER)] )                  , ( playerHunamConvict-                   , [(5, 3, "civilian")] )+                   , Just teamCivilian+                   , [(5, 3, CIVILIAN)] )                  , ( playerAnimalMagnificent                        { fleaderMode = LeaderUI $ AutoLeader True False                        , fhasUI = True }-                   , [(10, 20, "mobile animal")] )+                   , Nothing+                   , [(10, 20, MOBILE_ANIMAL)] )                  , ( playerAnimalExquisite-                   , [(15, 30, "mobile animal")] )-                 , (playerHorror, []) ] }+                   , Nothing+                   , [(15, 30, MOBILE_ANIMAL)] )+                 , (playerHorror, Nothing, []) ] }  rosterBattle = Roster   { rosterList = [ ( playerHero { fcanEscape = False                                 , fhiCondPoly = hiHeroLong }-                   , [(10, 5, "soldier hero")] )+                   , Just teamExplorer+                   , [(10, 5, SOLDIER_HERO)] )                  , ( playerMonster {fneverEmpty = True}-                   , [(10, 35, "mobile monster")] )+                   , Nothing+                   , [(10, 35, MOBILE_MONSTER)] )                  , ( playerAnimal {fneverEmpty = True}-                   , [(10, 20, "mobile animal")] )+                   , Nothing+                   , [(10, 20, MOBILE_ANIMAL)] )                  , ( playerRobot {fneverEmpty = True}-                   , [(10, 15, "mobile robot")] ) ]+                   , Nothing+                   , [(10, 15, MOBILE_ROBOT)] ) ]   , rosterEnemy = [ ("Spacefarer", "Alien Hierarchy")                   , ("Spacefarer", "Animal Kingdom")                   , ("Spacefarer", "Robot Anarchy") ]@@ -528,14 +745,18 @@                                 , fleaderMode =                                     LeaderAI $ AutoLeader False False                                 , fhasUI = False }-                   , [(10, 5, "soldier hero")] )+                   , Just teamExplorer+                   , [(10, 5, SOLDIER_HERO)] )                  , ( playerMonster { fneverEmpty = True                                    , fhasUI = True }-                   , [(10, 35, "mobile monster")] )+                   , Nothing+                   , [(10, 35, MOBILE_MONSTER)] )                  , ( playerAnimal {fneverEmpty = True}-                   , [(10, 20, "mobile animal")] )+                   , Nothing+                   , [(10, 20, MOBILE_ANIMAL)] )                  , ( playerRobot {fneverEmpty = True}-                   , [(10, 15, "mobile robot")] ) ] }+                   , Nothing+                   , [(10, 15, MOBILE_ROBOT)] ) ] }  rosterBattleSurvival = rosterBattle   { rosterList = [ ( playerHero { fcanEscape = False@@ -543,97 +764,112 @@                                 , fleaderMode =                                     LeaderAI $ AutoLeader False False                                 , fhasUI = False }-                   , [(10, 5, "soldier hero")] )+                   , Just teamExplorer+                   , [(10, 5, SOLDIER_HERO)] )                  , ( playerMonster {fneverEmpty = True}-                   , [(10, 35, "mobile monster")] )+                   , Nothing+                   , [(10, 35, MOBILE_MONSTER)] )                  , ( playerAnimal { fneverEmpty = True                                   , fhasUI = True }-                   , [(10, 20, "mobile animal")] )+                   , Nothing+                   , [(10, 20, MOBILE_ANIMAL)] )                  , ( playerRobot {fneverEmpty = True}-                   , [(10, 15, "mobile robot")] ) ] }+                   , Nothing+                   , [(10, 15, MOBILE_ROBOT)] ) ] }  rosterDefense = rosterCrawl   { rosterList = [ ( playerAntiHero-                   , [(3, 3, "crawl hero")] )+                   , Just teamExplorer+                   , [(3, 3, CRAWL_HERO)] )                  , ( playerAntiMonster+                   , Nothing                    , [] )                  , ( playerAnimal+                   , Nothing                    , -- Fun from the start to avoid empty initial level:-                     [ (3, 5 + 1 `d` 2, "animal")  -- many, because no spawning+                     [ (3, 5 + 1 `d` 2, ANIMAL)  -- many, because no spawning                      -- Optional huge battle at the end:-                     , (15, 100, "mobile animal") ] )+                     , (15, 100, MOBILE_ANIMAL) ] )                  , ( playerRobot+                   , Nothing                    , [] ) ] }  rosterDefenseEmpty = rosterCrawl   { rosterList = [ ( playerAntiMonster {fneverEmpty = True}-                   , [(4, 1, "scout monster")] )-                 , (playerHorror, []) ]  -- for spawned and summoned animals+                   , Nothing+                   , [(4, 1, SCOUT_MONSTER)] )+                 , (playerHorror, Nothing, []) ]+                     -- for spawned and summoned animals   , rosterEnemy = []   , rosterAlly = [] } -cavesRaid, cavesBrawl, cavesShootout, cavesHunt, cavesEscape, cavesZoo, cavesAmbush, cavesCrawl, cavesCrawlEmpty, cavesDig, cavesSee, cavesSafari, cavesBattle :: Caves+cavesRaid, cavesBrawl, cavesCrawl, cavesShootout, cavesHunt, cavesEscape, cavesZoo, cavesAmbush, cavesSafari, cavesDig, cavesSee, cavesShort, cavesCrawlEmpty, cavesBattle :: Caves -cavesRaid = [([3], ["caveRaid"])]+cavesRaid = [([2], [CAVE_RAID])] -cavesBrawl = [([4], ["caveBrawl"])]+cavesBrawl = reverse [([2], [CAVE_BRAWL_ALT]), ([3], [CAVE_BRAWL])] -cavesShootout = [([5], ["caveShootout"])]+listCrawl :: [([Int], [GroupName CaveKind])]+listCrawl =+  [ ([1], [CAVE_OUTERMOST])+  , ([2], [CAVE_SHALLOW_ROGUE])+  , ([3], [CAVE_BRIDGE])+  , ([4], [CAVE_NOISE])+  , ([7, 6, 5], [CAVE_ROGUE, CAVE_ARENA, CAVE_RESIDENTIAL])+       -- reversed order, to match @reverse@ later on+  , ([8], [CAVE_LABORATORY])+  , ([11, 10, 9], [DEFAULT_RANDOM, DEFAULT_RANDOM, CAVE_MUSEUM])+  , ([12], [CAVE_EXIT])+  , ([14, 13], [DEFAULT_RANDOM, CAVE_CASINO])+  , ([15], [CAVE_POWER]) ] -cavesHunt = [([7], ["caveHunt"])]+cavesCrawl = reverse listCrawl -cavesEscape = [([9], ["caveEscape"])]+cavesShootout = [([5], [CAVE_SHOOTOUT])] -cavesZoo = [([11], ["caveZoo"])]+cavesHunt = [([6], [CAVE_HUNT])] -cavesAmbush = [([13], ["caveAmbush"])]+cavesEscape = [([7], [CAVE_ESCAPE])] -listCrawl :: [([Int], [GroupName CaveKind])]-listCrawl =-  [ ([1], ["caveOutermost"])-  , ([2], ["caveShallowRogue"])-  , ([3], ["caveBridge"])-  , ([4], ["caveNoise"])-  , ([5], ["caveRogue"])-  , ([6], ["caveArena"])-  , ([7], ["caveResidential"])-       -- reversed order, to match @reverse@ later on-  , ([8], ["caveLaboratory"])-  , ([11, 10, 9], ["default random", "default random", "caveMuseum"])-  , ([12], ["caveExit"])-  , ([14, 13], ["default random", "caveCasino"])-  , ([15], ["cavePower"]) ]+cavesZoo = [([8], [CAVE_ZOO])] --- Reversed to have the last cave small and exactly in the middle--- of the screen.-cavesCrawl = reverse listCrawl+cavesAmbush = [([9], [CAVE_AMBUSH])] -cavesCrawlEmpty = reverse $-  map (\(ns, grps) ->-        (ns, if grps == ["caveBridge"] then ["caveShallowRogue"] else grps))-      listCrawl+cavesSafari = reverse [ ([5], [CAVE_SAFARI_1])+                      , ([10], [CAVE_SAFARI_2])+                      , ([15], [CAVE_SAFARI_3]) ] +cavesDig =+  reverse $ concat+  $ zipWith (map . renumberCaves)+            [0, 15 ..]+            (replicate 100 listCrawl)+--            [0, 2 ..]+--            (replicate 100 [([1], [CAVE_OUTERMOST]),([2], [CAVE_EXIT])])+ renumberCaves :: Int -> ([Int], [GroupName CaveKind])               -> ([Int], [GroupName CaveKind]) renumberCaves offset (ns, l) = (map (+ offset) ns, l) -cavesDig = reverse $ concat $ zipWith (map . renumberCaves)-                                      [0, 15 ..]-                                      (replicate 100 listCrawl)- cavesSee = let numberCaves n c = ([n], [c])            in reverse $ zipWith numberCaves [1..]-              $ concatMap (replicate 8) allCaves+              $ concatMap (replicate 10) allCaves +cavesShort = let numberCaves n c = ([n], [c])+             in reverse $ zipWith numberCaves [1..]+                $ concatMap (replicate 100) $ take 7 allCaves+ allCaves :: [GroupName CaveKind] allCaves =-  [ "caveRaid", "caveBrawl", "caveShootout", "caveHunt", "caveEscape", "caveZoo"-  , "caveAmbush"-  , "caveRogue", "caveResidential", "caveLaboratory", "caveArena", "caveCasino"-  , "caveMuseum", "caveNoise", "cavePower", "caveOutermost", "caveExit" ]+  [ CAVE_RAID, CAVE_BRAWL, CAVE_SHOOTOUT, CAVE_HUNT, CAVE_ESCAPE, CAVE_ZOO+  , CAVE_AMBUSH+  , CAVE_OUTERMOST, CAVE_SHALLOW_ROGUE, CAVE_BRIDGE, CAVE_NOISE, CAVE_ROGUE+  , CAVE_ARENA, CAVE_RESIDENTIAL, CAVE_LABORATORY, CAVE_MUSEUM, CAVE_EXIT+  , CAVE_CASINO, CAVE_POWER ] -cavesSafari = reverse [ ([5], ["caveSafari1"])-                      , ([10], ["caveSafari2"])-                      , ([15], ["caveSafari3"]) ]+cavesCrawlEmpty = reverse $+  map (\(ns, grps) ->+        (ns, if grps == [CAVE_BRIDGE] then [CAVE_SHALLOW_ROGUE] else grps))+      listCrawl -cavesBattle = [([10], ["caveBattle"])]+cavesBattle = [([10], [CAVE_BATTLE])]
GameDefinition/Content/ModeKindPlayer.hs view
@@ -1,5 +1,5 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE.@@ -18,21 +18,24 @@  import Game.LambdaHack.Core.Prelude -import Game.LambdaHack.Content.ModeKind-import Game.LambdaHack.Definition.Ability+import           Content.ItemKindActor+import           Content.ItemKindOrgan+import qualified Game.LambdaHack.Content.ItemKind as IK+import           Game.LambdaHack.Content.ModeKind+import           Game.LambdaHack.Definition.Ability  playerHero, playerAntiHero, playerCivilian, playerMonster, playerAntiMonster, playerAnimal, playerHorror, playerMonsterTourist, playerHunamConvict, playerAnimalMagnificent, playerAnimalExquisite :: Player playerRobot :: Player  playerHero = Player   { fname = "Spacefarer"-  , fgroups = ["hero"]+  , fgroups = [HERO]   , fskillsOther = meleeAdjacent   , fcanEscape = True   , fneverEmpty = True   , fhiCondPoly = hiHeroLong   , fhasGender = True-  , ftactic = TExplore+  , fdoctrine = TExplore   , fleaderMode = LeaderUI $ AutoLeader False False   , fhasUI = True   }@@ -44,26 +47,26 @@  playerCivilian = Player   { fname = "Civilian"-  , fgroups = ["hero", "civilian"]+  , fgroups = [HERO, CIVILIAN]   , fskillsOther = zeroSkills  -- not coordinated by any leadership   , fcanEscape = False   , fneverEmpty = True   , fhiCondPoly = hiHeroMedium   , fhasGender = True-  , ftactic = TPatrol+  , fdoctrine = TPatrol   , fleaderMode = LeaderNull  -- unorganized   , fhasUI = False   }  playerMonster = Player   { fname = "Alien Hierarchy"-  , fgroups = ["monster", "mobile monster", "aquatic monster"]+  , fgroups = [MONSTER, MOBILE_MONSTER, AQUATIC_MONSTER]   , fskillsOther = zeroSkills   , fcanEscape = False   , fneverEmpty = False   , fhiCondPoly = hiDweller   , fhasGender = False-  , ftactic = TExplore+  , fdoctrine = TExplore   , fleaderMode =       -- No point changing leader on level, since all move and they       -- don't follow the leader.@@ -78,14 +81,14 @@  playerAnimal = Player   { fname = "Animal Kingdom"-  , fgroups = [ "animal", "mobile animal", "immobile animal", "aquatic animal"-              , "scavenger" ]+  , fgroups = [ ANIMAL, MOBILE_ANIMAL, IMMOBILE_ANIMAL, AQUATIC_ANIMAL+              , SCAVENGER ]   , fskillsOther = zeroSkills   , fcanEscape = False   , fneverEmpty = False   , fhiCondPoly = hiDweller   , fhasGender = False-  , ftactic = TRoam  -- can't pick up, so no point exploring+  , fdoctrine = TRoam  -- can't pick up, so no point exploring   , fleaderMode = LeaderNull   , fhasUI = False   }@@ -97,13 +100,13 @@ -- should be present or a horror player should be added to host them. playerHorror = Player   { fname = "Horror Den"-  , fgroups = [horrorGroup]+  , fgroups = [IK.HORROR]   , fskillsOther = zeroSkills   , fcanEscape = False   , fneverEmpty = False   , fhiCondPoly = []   , fhasGender = False-  , ftactic = TPatrol  -- disoriented+  , fdoctrine = TPatrol  -- disoriented   , fleaderMode = LeaderNull   , fhasUI = False   }@@ -113,7 +116,7 @@                     , fcanEscape = True                     , fneverEmpty = True  -- no spawning                     , fhiCondPoly = hiHeroMedium-                    , ftactic = TFollow  -- follow-the-guide, as tourists do+                    , fdoctrine = TFollow  -- follow-the-guide, as tourists do                     , fleaderMode = LeaderUI $ AutoLeader False False }  playerHunamConvict =@@ -128,9 +131,6 @@   playerAnimal { fname = "Animal Exquisite Herds and Packs Galore"                , fneverEmpty = True } -victoryOutcomes :: [Outcome]-victoryOutcomes = [Conquer, Escape]- hiHeroLong, hiHeroMedium, hiHeroShort, hiDweller :: HiCondPoly  hiHeroShort =@@ -141,7 +141,7 @@   , ( [(HiSprint, -500)]  -- speed matters, but only if fast enough     , victoryOutcomes )   , ( [(HiSurvival, 10)]  -- few points for surviving long-    , [minBound..maxBound] \\ victoryOutcomes )+    , deafeatOutcomes )   ]  hiHeroMedium =@@ -154,7 +154,7 @@   , ( [(HiBlitz, -100)]  -- speed matters always     , victoryOutcomes )   , ( [(HiSurvival, 10)]  -- few points for surviving long-    , [minBound..maxBound] \\ victoryOutcomes )+    , deafeatOutcomes )   ]  -- Heroes in long crawls rejoice in loot.@@ -166,7 +166,7 @@   , ( [(HiBlitz, -100)]  -- speed matters always     , victoryOutcomes )   , ( [(HiSurvival, 10)]  -- few points for surviving long-    , [minBound..maxBound] \\ victoryOutcomes )+    , deafeatOutcomes )   ]  -- Spawners get no points from loot, but try to kill@@ -180,21 +180,22 @@             , ( [(HiBlitz, -100)]  -- speed matters               , victoryOutcomes )             , ( [(HiSurvival, 100)]-              , [minBound..maxBound] \\ victoryOutcomes )+              , deafeatOutcomes )             ]  -- Allure-specific  playerRobot = Player   { fname = "Robot Anarchy"-  , fgroups = [ "robot", "mobile robot", "immobile robot" --, "aquatic robot"-              , "construction robot" ]+  , fgroups = [ ROBOT, MOBILE_ROBOT, IMMOBILE_ROBOT  --, "aquatic robot"+              , CONSTRUCTION_ROBOT ]   , fskillsOther = zeroSkills   , fcanEscape = False   , fneverEmpty = False   , fhiCondPoly = hiDweller   , fhasGender = False-  , ftactic = TRoam  -- TODO:TFollow -- coordinated via net, follow alien leader+  , fdoctrine = TRoam+      -- TODO:TFollow -- coordinated via net, follow alien leader   , fleaderMode = LeaderNull   , fhasUI = False   }
GameDefinition/Content/PlaceKind.hs view
@@ -1,1595 +1,1878 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)--- This file is a part of the computer game Allure of the Stars--- and is released under the terms of the GNU Affero General Public License.--- For license and copyright information, see the file LICENSE.------ | Room, hall and passage definitions.-module Content.PlaceKind-  ( content-  ) where--import Prelude ()--import Game.LambdaHack.Core.Prelude--import qualified Data.Text as T--import Game.LambdaHack.Content.PlaceKind-import Game.LambdaHack.Content.TileKind (TileKind)-import Game.LambdaHack.Definition.Defs--content :: [PlaceKind]-content =-  [deadEnd, rect, rect2, rectWindows, glasshouse, glasshouse2, glasshouse3, glasshouse4, pulpit, ruin, ruin2, collapsed, collapsed2, collapsed3, collapsed4, collapsed5, collapsed6, collapsed7, pillar, pillar2, pillar3, pillar4, pillar5, pillar6, colonnade, colonnade2, colonnade3, colonnade4, colonnade5, colonnade6, colonnade7, colonnade8, colonnade9, colonnade10, lampPost, lampPost2, lampPost3, lampPost4, treeShade, fogClump, fogClump2, smokeClump, smokeClump2, smokeClump3FGround, bushClump, escapeDown, escapeDown2, escapeDown3, escapeDown4, escapeDown5, escapeDown6, escapeDown7, escapeDown8, escapeDown9, staircase1, staircase2, staircase3, staircase4, staircase5, staircase6, staircase7, staircase8, staircase9, staircase10, staircase11, staircase12, staircase13, staircase14, staircase15, staircase16, staircase17, staircase18, staircase19, staircase20, staircase21, staircase22, staircase23, staircase24, staircase25, staircase26, staircase27, staircase28, staircase29, staircase30, staircase31, staircase32, staircase33, staircase34, staircase35, staircase36, staircase37]-  -- Allure-specific-  ++ [staircaseLift11, staircaseLift12, staircaseLift13, staircaseLift14, staircaseLift15, staircaseLift16, staircaseLift17, staircaseLift18, staircaseLift19, staircaseLift20, staircaseLift21, staircaseLift22, staircaseLift23, staircaseLift24, staircaseLift25]-  -- automatically generated-  ++ generatedStairs ++ generatedEscapes-  -- Allure-specific, continued-  ++ [ pumps, oval, ovalFloor, ovalSquare, ovalBasin, ovalBasin2, squareBasin, squareBasin2, floodedRoom, maze, maze2, maze3, mazeBig, mazeBig2, cells, cells2, cells3, cells4, cells5, cells6, cells7, tank, tank2, tank3, tank4, tank5, shuttleHusk, shuttleHusk2, shuttleHusk3, shuttleHusk4, shuttleHusk5, shuttleHusk6]--deadEnd,    rect, rect2, rectWindows, glasshouse, glasshouse2, glasshouse3, glasshouse4, pulpit, ruin, ruin2, collapsed, collapsed2, collapsed3, collapsed4, collapsed5, collapsed6, collapsed7, pillar, pillar2, pillar3, pillar4, pillar5, pillar6, colonnade, colonnade2, colonnade3, colonnade4, colonnade5, colonnade6, colonnade7, colonnade8, colonnade9, colonnade10, lampPost, lampPost2, lampPost3, lampPost4, treeShade, fogClump, fogClump2, smokeClump, smokeClump2, smokeClump3FGround, bushClump, escapeDown, escapeDown2, escapeDown3, escapeDown4, escapeDown5, escapeDown6, escapeDown7, escapeDown8, escapeDown9, staircase1, staircase2, staircase3, staircase4, staircase5, staircase6, staircase7, staircase8, staircase9, staircase10, staircase11, staircase12, staircase13, staircase14, staircase15, staircase16, staircase17, staircase18, staircase19, staircase20, staircase21, staircase22, staircase23, staircase24, staircase25, staircase26, staircase27, staircase28, staircase29, staircase30, staircase31, staircase32, staircase33, staircase34, staircase35, staircase36, staircase37 :: PlaceKind--- Allure-specific-staircaseLift11, staircaseLift12, staircaseLift13, staircaseLift14, staircaseLift15, staircaseLift16, staircaseLift17, staircaseLift18, staircaseLift19, staircaseLift20, staircaseLift21, staircaseLift22, staircaseLift23, staircaseLift24, staircaseLift25, pumps, oval, ovalFloor, ovalSquare, ovalBasin, ovalBasin2, squareBasin, squareBasin2, floodedRoom, maze, maze2, maze3, mazeBig, mazeBig2, cells, cells2, cells3, cells4, cells5, cells6, cells7, tank, tank2, tank3, tank4, tank5, shuttleHusk, shuttleHusk2, shuttleHusk3, shuttleHusk4, shuttleHusk5, shuttleHusk6 :: PlaceKind--staircase, staircaseLift :: PlaceKind  -- templates--staircaseBasic :: [PlaceKind]-staircaseBasic = [staircase1, staircase2, staircase3, staircase4, staircase5, staircase6, staircase7, staircase8, staircase9, staircase10, staircase11, staircase12, staircase13, staircase14, staircase15, staircase16, staircase17, staircase18, staircase19, staircase20, staircase21, staircase22, staircase23, staircase24, staircase25, staircase26, staircase27, staircase28, staircase29, staircase30, staircase31, staircase32, staircase33, staircase34, staircase35, staircase36, staircase37]-  -- Allure-specific-  ++ [staircaseLift11, staircaseLift12, staircaseLift13, staircaseLift14, staircaseLift15, staircaseLift16, staircaseLift17, staircaseLift18, staircaseLift19, staircaseLift20, staircaseLift21, staircaseLift22, staircaseLift23, staircaseLift24, staircaseLift25]--generatedStairs :: [PlaceKind]-generatedStairs =-  let (stairs, lifts) = partition ((/= "a lift") . pname) staircaseBasic-      gatedStairs = map switchStaircaseToGated stairs-      gatedLifts = map switchLiftToGated lifts-      decontaminatingStairs = map switchStaircaseToDecontaminating stairs-      decontaminatingLifts = map switchLiftToDecontaminating lifts-      weldedStairs = map switchStaircaseToWelded stairs-      weldedLifts = map switchLiftToWelded lifts-      outdoorStairs = map switchStaircaseToOutdoor stairs-      stairsAll = stairs ++ gatedStairs ++ decontaminatingStairs ++ weldedStairs-                  ++ outdoorStairs-      liftsAll = lifts ++ gatedLifts ++ decontaminatingLifts ++ weldedLifts-  in gatedStairs ++ gatedLifts-     ++ decontaminatingStairs ++ decontaminatingLifts-     ++ weldedStairs ++ weldedLifts-     ++ outdoorStairs-     ++ map (switchExitToUp "stair terminal") stairsAll-     ++ map (switchExitToUp "lift terminal") liftsAll-     ++ map (switchExitToDown "stair terminal") stairsAll-     ++ map (switchExitToDown "lift terminal") liftsAll--escapeDownBasic :: [PlaceKind]-escapeDownBasic =-  [ escapeDown, escapeDown2, escapeDown3, escapeDown4, escapeDown5, escapeDown6-  , escapeDown7, escapeDown8, escapeDown9 ]--generatedEscapes :: [PlaceKind]-generatedEscapes =-  let upEscapes = map switchEscapeToUp escapeDownBasic-      outdoorEscapes = map switchEscapeToOutdoorDown escapeDownBasic-      spaceshipEscapes = map switchEscapeToSpaceshipDown escapeDownBasic-  in upEscapes ++ outdoorEscapes ++ spaceshipEscapes---- The dots below are @Char.chr 183@, as defined in @TileKind.floorSymbol@.-deadEnd = PlaceKind  -- needs to have index 0-  { psymbol  = 'd'-  , pname    = "a dead end"-  , pfreq    = []-  , prarity  = []-  , pcover   = CStretch-  , pfence   = FNone-  , ptopLeft = ["·"]-  , poverrideDark = []-  , poverrideLit = []-  }-rect = PlaceKind  -- Valid for any nonempty area, hence low frequency.-  { psymbol  = 'r'-  , pname    = "a room"-  , pfreq    = [("rogue", 100), ("laboratory", 10)]-  , prarity  = [(1, 10), (10, 6)]-  , pcover   = CStretch-  , pfence   = FWall-  , ptopLeft = ["·"]-  , poverrideDark = []-  , poverrideLit = []-  }-rect2 = rect-  { pname    = "a pen"-  , pfreq    = [("shootout", 1), ("zoo", 10)]-  }-rectWindows = PlaceKind-  { psymbol  = 'w'-  , pname    = "a shed"-  , pfreq    = [("brawl", 12), ("escape", 20)]-  , prarity  = [(1, 10), (10, 10)]-  , pcover   = CStretch-  , pfence   = FNone-  , ptopLeft = [ "#%"-               , "%·"-               ]-  , poverrideDark = [('%', "rectWindowsOver_%")]-  , poverrideLit = [('%', "rectWindowsOver_%")]-  }-glasshouse = PlaceKind-  { psymbol  = 'g'-  , pname    = "a glasshouse"-  , pfreq    = [("shootout", 8)]-  , prarity  = [(1, 10), (10, 7)]-  , pcover   = CStretch-  , pfence   = FNone-  , ptopLeft = [ "%%"-               , "%·"-               ]-  , poverrideDark = []-  , poverrideLit = []-  }-glasshouse2 = glasshouse-  { pname    = "a glass cage"-  , pfreq    = [("laboratory", 2), ("zoo", 30)]-  }-glasshouse3 = glasshouse-  { pname    = "an entertainment center"-  , pfreq    = [("arena", 1), ("ambush", 10)]-  }-glasshouse4 = glasshouse-  { pname    = "an exhibition area"-  , pfreq    = [("arena", 1), ("museum", 1)]-  }-pulpit = PlaceKind-  { psymbol  = 'p'-  , pname    = "a stand podium"-  , pfreq    = [("arena", 15), ("museum", 15), ("zoo", 80)]-  , prarity  = [(1, 1)]-  , pcover   = CMirror-  , pfence   = FGround-  , ptopLeft = [ "%%·"-               , "%··"-               , "··0"-               ]-  , poverrideDark = [('0', "pulpit")]-  , poverrideLit = [('0', "pulpit")]-      -- except for floor, this will all be lit, regardless of night/dark; OK-  }-ruin = PlaceKind-  { psymbol  = 'R'-  , pname    = "ruins"-  , pfreq    = [("battle", 660), ("ambush", 70)]-  , prarity  = [(1, 1)]-  , pcover   = CStretch-  , pfence   = FWall-  , ptopLeft = ["X"]-  , poverrideDark = []-  , poverrideLit = []-  }-ruin2 = ruin-  { pname    = "a scaffolding"-  , pfreq    = [("noise", 2000), ("exit", 5), ("museum", 1)]-  }-collapsed = PlaceKind-  { psymbol  = 'c'-  , pname    = "a hardware stack"-  , pfreq    = [("noise", 1)]-      -- no point taking up space if very little space taken,-      -- but if no other place can be generated, a failsafe is useful-  , prarity  = [(1, 1)]-  , pcover   = CStretch-  , pfence   = FNone-  , ptopLeft = [ "#"-               ]-  , poverrideDark = [('#', "doorlessMachineryOver_#")]-  , poverrideLit = [('#', "doorlessMachineryOver_#")]-  }-collapsed2 = collapsed-  { pfreq    = [("noise", 1000), ("battle", 200)]-  , ptopLeft = [ "X#"-               , "##"-               ]-  }-collapsed3 = collapsed-  { pfreq    = [("noise", 2000), ("battle", 200)]-  , ptopLeft = [ "XX#"-               , "###"-               ]-  }-collapsed4 = collapsed-  { pfreq    = [("noise", 2200), ("battle", 200)]-  , ptopLeft = [ "XXX#"-               , "####"-               ]-  }-collapsed5 = collapsed-  { pfreq    = [("noise", 3000), ("battle", 500)]-  , ptopLeft = [ "XX#"-               , "X##"-               , "###"-               ]-  }-collapsed6 = collapsed-  { pfreq    = [("noise", 4000), ("battle", 1000)]-  , ptopLeft = [ "XXX#"-               , "X###"-               , "####"-               ]-  }-collapsed7 = collapsed-  { pfreq    = [("noise", 4000), ("battle", 1000)]-  , ptopLeft = [ "XXX#"-               , "XX##"-               , "####"-               ]-  }-pillar = PlaceKind-  { psymbol  = 'p'-  , pname    = "a market"-  , pfreq    = [("rogue", 300), ("arena", 10000), ("empty", 400)]-  , prarity  = [(1, 1)]-  , pcover   = CStretch-  , pfence   = FWall-  -- Larger rooms require support pillars.-  , ptopLeft = [ "····"-               , "·0··"-               , "····"-               , "····"-               ]-  , poverrideDark = []-  , poverrideLit = []-  }-pillar2 = pillar-  { pname    = "a mall"-  , pfreq    = [("rogue", 10000), ("arena", 100000), ("empty", 5000)]-  , ptopLeft = [ "0····"-               , "·····"-               , "·····"-               , "···0·"-               , "····~"-               ]-  , poverrideDark = [('~', "poolOver_~_Dark")]-  , poverrideLit = [('~', "poolOver_~_Lit")]-  }-pillar3 = pillar-  { pname    = "a court"-  , pfreq    = [ ("rogue", 250), ("arena", 15), ("museum", 10)-               , ("laboratory", 200) ]-  , ptopLeft = [ "#··"-               , "···"-               , "···"-               ]-  }-pillar4 = pillar-  { pname    = "a plaza"-  , pfreq    = [ ("rogue", 1500), ("arena", 5000)-               , ("museum", 4000), ("laboratory", 1500) ]-  , ptopLeft = [ "#·#·"-               , "····"-               , "#···"-               , "····"-               ]-  }-pillar5 = pillar-  { pname    = "a bank outlet"-  , pfreq    = [ ("rogue", 1200), ("arena", 6000)-               , ("empty", 600), ("exit", 600) ]-  , ptopLeft = [ "&i%·"-               , "ii#·"-               , "%#+·"-               , "····"-               ]-  , poverrideDark = [ ('&', "cache deposit"), ('+', "trapped door")-                    , ('i', "floorActorItem") ]  -- lit or not, randomly-  , poverrideLit = [ ('&', "cache deposit"), ('+', "trapped door")-                   , ('i', "floorActorItem") ]  -- lit or not, randomly-  }-pillar6 = pillar-  { pname    = "a jewelry store"-  , pfreq    = [ ("rogue", 1200), ("arena", 6000)-               , ("museum", 7000), ("empty", 500) ]-  , ptopLeft = [ "0f··"-               , "ff%·"-               , "·%&·"-               , "····"-               ]-  , poverrideDark = [ ('&', "cache jewelry"), ('0', "lampPostOver_0")-                    , ('f', "floorActorLit") ]-  , poverrideLit = [ ('&', "cache jewelry"), ('0', "lampPostOver_0")-                   , ('f', "floorActorLit") ]-  }-colonnade = PlaceKind-  { psymbol  = 'c'-  , pname    = "a colonnade"-  , pfreq    = [ ("rogue", 12), ("noise", 1000), ("escape", 200)-               , ("exit", 180) ]-  , prarity  = [(1, 12), (10, 12)]-  , pcover   = CAlternate-  , pfence   = FFloor-  , ptopLeft = [ "#·"-               , "··"-               ]-  , poverrideDark = []-  , poverrideLit = []-  }-colonnade2 = colonnade-  { pfreq    = [("rogue", 300)]-  , prarity  = [(1, 1)]-  , pfence   = FWall-  , ptopLeft = [ "#·"-               , "·#"-               ]-  }-colonnade3 = colonnade-  { prarity  = [(1, 120), (10, 120)]-  , ptopLeft = [ "··#"-               , "·#·"-               , "#··"-               ]-  }-colonnade4 = colonnade-  { prarity  = [(1, 1)]-  , pfreq    = [("rogue", 1000)]-  , pfence   = FWall-  , ptopLeft = [ "#··"-               , "·#·"-               , "··#"-               ]-  }-colonnade5 = colonnade-  { prarity  = [(1, 25), (10, 25)]-  , ptopLeft = [ "#··"-               , "··#"-               ]-  }-colonnade6 = colonnade-  { prarity  = [(1, 14), (10, 14)]-  , ptopLeft = [ "#·"-               , "··"-               , "·#"-               ]-  }-colonnade7 = colonnade-  { pfreq    = [("arena", 50), ("museum", 30), ("empty", 800)]-  , prarity  = [(1, 7), (10, 7)]-  , ptopLeft = [ "0·"-               , "··"-               ]-  }-colonnade8 = colonnade7-  { prarity  = [(1, 50), (10, 50)]-  , ptopLeft = [ "··0"-               , "·0·"-               , "0··"-               ]-  }-colonnade9 = colonnade7-  { prarity  = [(1, 20), (10, 20)]-  , ptopLeft = [ "0··"-               , "··0"-               ]-  }-colonnade10 = colonnade7-  { prarity  = [(1, 10), (10, 10)]-  , ptopLeft = [ "0·"-               , "··"-               , "·0"-               ]-  }-lampPost = PlaceKind-  { psymbol  = 'l'-  , pname    = "a lamp-lit area"-  , pfreq    = [ ("escape", 200), ("zoo", 100), ("ambush", 1000)-               , ("battle", 100) ]-  , prarity  = [(1, 1)]-  , pcover   = CVerbatim-  , pfence   = FNone-  , ptopLeft = [ "X·X"-               , "·0·"-               , "X·X"-               ]-  , poverrideDark = [('0', "lampPostOver_0"), ('·', "floorActorLit")]-  , poverrideLit = [('0', "lampPostOver_0"), ('·', "floorActorLit")]-  }-lampPost2 = lampPost-  { ptopLeft = [ "···"-               , "·0·"-               , "···"-               ]-  }-lampPost3 = lampPost-  { pfreq    = [("escape", 3000), ("zoo", 500), ("battle", 1100)]-  , ptopLeft = [ "XX·XX"-               , "X···X"-               , "··0··"-               , "X···X"-               , "XX·XX"-               ]-  }-lampPost4 = lampPost-  { pfreq    = [("escape", 3000), ("zoo", 500), ("battle", 600)]-  , ptopLeft = [ "X···X"-               , "·····"-               , "··0··"-               , "·····"-               , "X···X"-               ]-  }-treeShade = PlaceKind-  { psymbol  = 't'-  , pname    = "a tree shade"-  , pfreq    = [("brawl", 500)]-  , prarity  = [(1, 1)]-  , pcover   = CMirror-  , pfence   = FNone-  , ptopLeft = [ "··s"-               , "s0·"-               , "Xs·"-               ]-  , poverrideDark = [ ('0', "treeShadeOver_0_Dark")-                    , ('s', "treeShadeOver_s_Dark")-                    , ('·', "shaded ground") ]-  , poverrideLit = [ ('0', "treeShadeOver_0_Lit")-                   , ('s', "treeShadeOver_s_Lit")-                   , ('·', "shaded ground") ]-  }-fogClump = PlaceKind-  { psymbol  = 'f'-  , pname    = "a foggy patch"-  , pfreq    = [("empty", 400), ("shootout", 70), ("escape", 60)]-  , prarity  = [(1, 1)]-  , pcover   = CMirror-  , pfence   = FNone-  , ptopLeft = [ "f;"-               , ";f"-               , ";X"-               ]-  , poverrideDark = [('f', "fogClumpOver_f_Dark"), (';', "fog Lit")]-  , poverrideLit = [('f', "fogClumpOver_f_Lit"), (';', "fog Lit")]-  }-fogClump2 = fogClump-  { pfreq    = [("empty", 3000), ("shootout", 400), ("escape", 100)]-  , ptopLeft = [ "X;f"-               , "f;f"-               , ";;f"-               , "Xff"-               ]-  }-smokeClump = PlaceKind-  { psymbol  = 's'-  , pname    = "a smoky patch"-  , pfreq    = [("exit", 50), ("zoo", 40), ("ambush", 50)]-  , prarity  = [(1, 1)]-  , pcover   = CMirror-  , pfence   = FNone-  , ptopLeft = [ "f;"-               , ";f"-               , ";X"-               ]-  , poverrideDark = [ ('f', "smokeClumpOver_f_Dark"), (';', "smoke Lit")-                    , ('·', "floorActorDark") ]-  , poverrideLit = [ ('f', "smokeClumpOver_f_Lit"), (';', "smoke Lit")-                   , ('·', "floorActorLit") ]-  }-smokeClump2 = smokeClump-  { pfreq    = [("exit", 300), ("zoo", 200), ("ambush", 150)]-  , ptopLeft = [ "X;f"-               , "f;f"-               , ";;f"-               , "Xff"-               ]-  }-smokeClump3FGround = smokeClump-  { pname    = "a burned out area"-  , pfreq    = [("laboratory", 25)]-  , prarity  = [(1, 1)]-  , pcover   = CVerbatim-  , pfence   = FGround-  , ptopLeft = [ ";f;"-               , "f·f"-               , "f·f"-               , ";f;"-               ]-      -- should not be used in caves with trails, because bushes should-      -- not grow over such artificial trails-  }-bushClump = PlaceKind-  { psymbol  = 'b'-  , pname    = "a bushy patch"-  , pfreq    = [("shootout", 100)]-  , prarity  = [(1, 1)]-  , pcover   = CMirror-  , pfence   = FNone-  , ptopLeft = [ "f;"-               , ";X"  -- one sure exit needed not to block a corner-               , ";f"-               ]-  , poverrideDark = [('f', "bushClumpOver_f_Dark"), (';', "bush Lit")]-  , poverrideLit = [('f', "bushClumpOver_f_Lit"), (';', "bush Lit")]-      -- should not be used in caves with trails, because bushes can't-      -- grow over such artificial trails-  }-escapeDown = PlaceKind-  { psymbol  = '>'-  , pname    = "an escape down"-  , pfreq    = [("escape down", 1)]-  , prarity  = [(1, 1)]-  , pcover   = CVerbatim-  , pfence   = FGround-  , ptopLeft = [ ">"-               ]-  , poverrideDark = [ ('*', "oil spill"), ('g', "frozen path")-                    , ('0', "lampPostOver_0")-                    , ('f', "floorActorLit"), ('r', "rubbleOrWaste_Dark") ]-  , poverrideLit = [ ('*', "oil spill"), ('g', "frozen path")-                   , ('0', "lampPostOver_0")-                   , ('f', "floorActorLit"), ('r', "rubbleOrWaste_Lit") ]-  }-escapeDown2 = escapeDown-  { pfreq    = [("escape down", 200)]-  , pfence   = FFloor-  , ptopLeft = [ "#·#"-               , "·>·"-               , "#·#"-               ]-  }-escapeDown3 = escapeDown-  { pfreq    = [("escape down", 200)]-  , pfence   = FFloor-  , ptopLeft = [ "·#·"-               , "#>#"-               , "·#·"-               ]-  }-escapeDown4 = escapeDown-  { pfreq    = [("escape down", 200)]-  , pfence   = FWall-  , ptopLeft = [ "^·^"-               , "·>·"-               , "^·^"-               ]-  }-escapeDown5 = escapeDown-  { pfreq    = [("escape down", 200)]-  , pcover   = CMirror-  , pfence   = FFloor-  , ptopLeft = [ "r#·"-               , "r>#"-               , "rrr"-               ]-  }-escapeDown6 = escapeDown-  { pfreq    = [("escape down", 1000)]-  , pfence   = FWall-  , ptopLeft = [ "··#··"-               , "·#*#·"-               , "#*>*#"-               , "·#*#·"-               , "··#··"-               ]-  }-escapeDown7 = escapeDown-  { pfreq    = [("escape down", 1000)]-  , pfence   = FFloor-  , ptopLeft = [ "·*#*·"-               , "*#*#*"-               , "#*>*#"-               , "*#*#*"-               , "·*#*·"-               ]-  }-escapeDown8 = escapeDown-  { pfreq    = [("escape down", 1000)]-  , pcover   = CMirror-  , pfence   = FWall-  , ptopLeft = [ "··#g·"-               , "·#gg·"-               , "·#>g#"-               , "·gg#·"-               , "g·#··"-               ]-  }-escapeDown9 = escapeDown-  { pfreq    = [("escape down", 1000)]-  , pcover   = CMirror-  , pfence   = FFloor-  , ptopLeft = [ "·f·#"-               , "%f>·"-               , "%0f·"-               , "ff%%"-               ]-  }-staircase = PlaceKind-  { psymbol  = '/'-  , pname    = "a staircase"-  , pfreq    = [("tiny staircase", 1)]  -- no cover when arriving; low freq-  , prarity  = [(1, 100), (10, 100)]-  , pcover   = CVerbatim-  , pfence   = FGround-  , ptopLeft = [ "<S>"-               ]-  , poverrideDark = [ ('<', "staircase up"), ('>', "staircase down")-                    , ('I', "signboard"), ('S', "fillerWall") ]-  , poverrideLit = [ ('<', "staircase up"), ('>', "staircase down")-                   , ('I', "signboard"), ('S', "fillerWall") ]-  }-staircase1 = staircase-  { prarity  = [(1, 1)]  -- no cover when arriving; so low rarity-  }-staircase2 = staircase-  { pfreq    = [("tiny staircase", 3)]-  , prarity  = [(1, 1)]-  , pfence   = FGround-  , ptopLeft = [ "·<S>·"-               ]-  }--- Allure-specific:-staircaseLift = PlaceKind-  { psymbol  = '|'-  , pname    = "a lift"-  , pfreq    = [("tiny lift", 1)]-  , prarity  = [(1, 100), (10, 100)]-  , pcover   = CVerbatim-  , pfence   = FFloor-  , ptopLeft = [ "<S>"-               ]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircase3 = staircaseLift-  { prarity  = [(1, 1)]-  }-staircase4 = staircaseLift-  { pfreq    = [("tiny lift", 3)]-  , prarity  = [(1, 1)]-  , ptopLeft = [ "·<S>·"-               ]-  }-staircase5 = staircase-  { pfreq    = [("open staircase", 200)]  -- no cover, open-  , pfence   = FGround-  , ptopLeft = [ "#·#"-               , "···"-               , "<S>"-               , "···"-               , "#·#"-               ]-  }-staircase6 = staircaseLift-  { pfreq    = [("open lift", 300)]-  , pfence   = FGround-  , ptopLeft = [ "#·#·#"-               , "·····"-               , "·<S>·"-               , "·····"-               , "#·#·#"-               ]-  }-staircase7 = staircase-  { pfreq    = [("open staircase", 500)]-  , pfence   = FGround-  , ptopLeft = [ "#·#·#·#"-               , "·······"-               , "#·<S>·#"-               , "·······"-               , "#·#·#·#"-               ]-  }-staircase8 = staircaseLift-  { pfreq    = [("open lift", 2000)]-  , pfence   = FGround-  , ptopLeft = [ "·#·#·#·"-               , "#·····#"-               , "··<S>··"-               , "#·····#"-               , "·#·#·#·"-               ]-  }-staircase9 = staircase-  { pfreq    = [("open staircase", 500)]-  , pfence   = FGround-  , ptopLeft = [ "#·······#"-               , "···<S>···"-               , "#·······#"-               ]-  }-staircase10 = staircaseLift-  { pfreq    = [("open lift", 500)]-  , pfence   = FGround-  , ptopLeft = [ "0·····0"-               , "··<S>··"-               , "0·····0"-               ]-  }-staircase11 = staircase-  { pfreq    = [("closed staircase", 2000)]  -- weak cover, low freq-  , pfence   = FFloor-  , ptopLeft = [ "·#·"-               , "#·#"-               , "···"-               , "<S>"-               , "···"-               , "#·#"-               , "·#·"-               ]-  }-staircase12 = staircase-  { pfreq    = [("closed staircase", 4000)]-  , pfence   = FFloor-  , ptopLeft = [ "·#·#·"-               , "#·#·#"-               , "·····"-               , "·<S>·"-               , "·····"-               , "#·#·#"-               , "·#·#·"-               ]-  }-staircase13 = staircase-  { pfreq    = [("closed staircase", 6000)]-  , pfence   = FFloor-  , ptopLeft = [ "·#·#·#·"-               , "#·#·#·#"-               , "·······"-               , "0·<S>·0"-               , "·······"-               , "#·#·#·#"-               , "·#·#·#·"-               ]-  }-staircase14 = staircase-  { pfreq    = [("closed staircase", 10000)]-  , pfence   = FFloor-  , ptopLeft = [ "#·#·#·#"-               , "·#·#·#·"-               , "#·····#"-               , "··<S>··"-               , "#·····#"-               , "·#·#·#·"-               , "#·#·#·#"-               ]-  }-staircase15 = staircase-  { pfreq    = [("closed staircase", 20000)]-  , pfence   = FFloor-  , ptopLeft = [ "·#·#·#·#·"-               , "#·#·#·#·#"-               , "·#~~~~~#·"-               , "#~~<S>~~#"-               , "·#~~~~~#·"-               , "#·#·#·#·#"-               , "·#·#·#·#·"-               ]-  }-staircase16 = staircase-  { pfreq    = [("closed staircase", 20000)]-  , pfence   = FFloor-  , ptopLeft = [ "#·#·#·#·#"-               , "·#·#·#·#·"-               , "#·······#"-               , "·#·<S>·#·"-               , "#·······#"-               , "·#·#·#·#·"-               , "#·#·#·#·#"-               ]-  }-staircase17 = staircase-  { pfreq    = [("closed staircase", 20000)]-  , pfence   = FFloor-  , ptopLeft = [ "#·#·#·#·#·#"-               , "·#·#·#·#·#·"-               , "#·#·····#·#"-               , "·#··<S>··#·"-               , "#·#·····#·#"-               , "·#·#·#·#·#·"-               , "#·#·#·#·#·#"-               ]-  }-staircase18 = staircase-  { pfreq    = [("closed staircase", 80000)]-  , pfence   = FFloor-  , ptopLeft = [ "XX#·#·#·#XX"-               , "X#·#·#·#·#X"-               , "#·#·····#·#"-               , "·#··<S>··#·"-               , "#·#·····#·#"-               , "X#·#·#·#·#X"-               , "XX#·#·#·#XX"-               ]-  }-staircase19 = staircase-  { pfreq    = [("closed staircase", 20000)]-  , pfence   = FFloor-  , ptopLeft = [ "·#·#·#·#·#·"-               , "#·#·#·#·#·#"-               , "·#·······#·"-               , "#·#·<S>·#·#"-               , "·#·······#·"-               , "#·#·#·#·#·#"-               , "·#·#·#·#·#·"-               ]-  }-staircase20 = staircase-  { pfreq    = [("closed staircase", 5000)]-  , pfence   = FFloor-  , ptopLeft = [ "·#·#·0·#·#·"-               , "#·#·····#·#"-               , "·#··<S>··#·"-               , "#·#·····#·#"-               , "·#·#·I·#·#·"-               ]-  }-staircase21 = staircase-  { pfreq    = [("closed staircase", 5000)]-  , pfence   = FFloor-  , ptopLeft = [ "#·#·I·#·#"-               , "·#·····#·"-               , "#··<S>··#"-               , "·#·····#·"-               , "#·#·0·#·#"-               ]-  }-staircase22 = staircase-  { pfreq    = [("closed staircase", 2000)]-  , pfence   = FFloor-  , ptopLeft = [ "#·#·····#·#"-               , "·#··<S>··#·"-               , "#·#·····#·#"-               ]-  }-staircase23 = staircase-  { pfreq    = [("closed staircase", 1000)]-  , pfence   = FFloor-  , ptopLeft = [ "·#·······#·"-               , "#·#·<S>·#·#"-               , "·#·······#·"-               ]-  }-staircase24 = staircase-  { pfreq    = [("closed staircase", 1000)]-  , pfence   = FFloor-  , ptopLeft = [ "·#·····#·"-               , "#··<S>··#"-               , "·#·····#·"-               ]-  }-staircase25 = staircase-  { pfreq    = [("walled staircase", 100)]-  , pfence   = FWall-  , ptopLeft = [ "·····"-               , "·<S>·"-               , "·····"-               ]-  }-staircase26 = staircase-  { pfreq    = [("walled staircase", 200)]-  , pfence   = FWall-  , ptopLeft = [ "·······"-               , "··<S>··"-               , "·······"-               ]-  }-staircase27 = staircaseLift-  { pfreq    = [("walled lift", 500)]-  , pfence   = FWall-  , ptopLeft = [ "#·····#"-               , "··<S>··"-               , "#·····#"-               ]-  }-staircase28 = staircaseLift-  { pfreq    = [("walled lift", 1000)]-  , pfence   = FWall-  , ptopLeft = [ "·····"-               , "·····"-               , "·<S>·"-               , "·····"-               , "·····"-               ]-  }-staircase29 = staircase-  { pfreq    = [("walled staircase", 1000)]-  , pfence   = FWall-  , ptopLeft = [ "#···#"-               , "·····"-               , "·<S>·"-               , "·····"-               , "#···#"-               ]-  }-staircase30 = staircaseLift-  { pfreq    = [("walled lift", 1000)]-  , pfence   = FWall-  , ptopLeft = [ "#···#"-               , "·····"-               , "·<S>·"-               , "·····"-               , "#···#"-               ]-  }-staircase31 = staircase-  { pfreq    = [("walled staircase", 2000)]-  , pfence   = FWall-  , ptopLeft = [ "·······"-               , "·~~~~~·"-               , "·~<S>~·"-               , "·~~~~~·"-               , "·······"-               ]-  }-staircase32 = staircaseLift-  { pfreq    = [("walled lift", 5000)]-  , pfence   = FWall-  , ptopLeft = [ "#·····#"-               , "·······"-               , "··<S>··"-               , "·······"-               , "#·····#"-               ]-  }-staircase33 = staircase-  { pfreq    = [("walled staircase", 5000)]-  , pfence   = FWall-  , ptopLeft = [ "#·#·#·#"-               , "·······"-               , "#·<S>·#"-               , "·······"-               , "#·#·#·#"-               ]-  }-staircase34 = staircaseLift-  { pfreq    = [("walled lift", 5000)]-  , pfence   = FWall-  , ptopLeft = [ "·#·#·#·"-               , "#·····#"-               , "··<S>··"-               , "#·····#"-               , "·#·#·#·"-               ]-  }-staircase35 = staircase-  { pfreq    = [("walled staircase", 1000)]-  , pfence   = FWall-  , ptopLeft = [ "·········"-               , "···<S>···"-               , "·········"-               ]-  }-staircase36 = staircaseLift-  { pfreq    = [("walled lift", 1000)]-  , pfence   = FWall-  , ptopLeft = [ "·#·····#·"-               , "#··<S>··#"-               , "·#·····#·"-               ]-  }-staircase37 = staircase-  { pfreq    = [("walled staircase", 1000)]-  , pfence   = FWall-  , ptopLeft = [ "·········"-               , "·0·<S>·0·"-               , "·········"-               ]-  }---- * Allure-specific--overrideLift :: [(Char, GroupName TileKind)]-overrideLift = [ ('<', "staircase lift up"), ('>', "staircase lift down")-               , ('I', "signboard"), ('S', "lift shaft") ]-staircaseLift11 = staircase11-  { pname     = "a lift"-  , pfreq     = [("closed lift", 2000)]  -- weak cover, low freq-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift12 = staircase12-  { pname     = "a lift"-  , pfreq     = [("closed lift", 4000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift13 = staircase13-  { pname     = "a lift"-  , pfreq     = [("closed lift", 6000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift14 = staircase14-  { pname     = "a lift"-  , pfreq     = [("closed lift", 10000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift15 = staircase15-  { pname     = "a lift"-  , pfreq     = [("closed lift", 20000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift16 = staircase16-  { pname     = "a lift"-  , pfreq     = [("closed lift", 20000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift17 = staircase17-  { pname     = "a lift"-  , pfreq     = [("closed lift", 20000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift18 = staircase18-  { pname     = "a lift"-  , pfreq     = [("closed lift", 80000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift19 = staircase19-  { pname     = "a lift"-  , pfreq     = [("closed lift", 20000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift20 = staircase20-  { pname     = "a lift"-  , pfreq     = [("closed lift", 5000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift21 = staircase21-  { pname     = "a lift"-  , pfreq     = [("closed lift", 5000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift22 = staircase22-  { pname     = "a lift"-  , pfreq     = [("closed lift", 2000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift23 = staircase23-  { pname     = "a lift"-  , pfreq     = [("closed lift", 1000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift24 = staircase24-  { pname     = "a lift"-  , pfreq     = [("closed lift", 1000)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-staircaseLift25 = staircase25-  { pname     = "a lift"-  , pfreq     = [("walled lift", 100)]-  , poverrideDark = overrideLift-  , poverrideLit = overrideLift-  }-pumps = PlaceKind-  { psymbol  = 'w'-  , pname    = "water pumps"-  , pfreq    = [ ("rogue", 200), ("laboratory", 100), ("empty", 2000)-               , ("brawl", 80), ("shootout", 50) ]-  , prarity  = [(1, 1)]-  , pcover   = CAlternate-  , pfence   = FWall-  , ptopLeft = [ "·f"-               , "%·"-               ]-  , poverrideDark = [ ('%', "doorlessMachineryOver_#")-                    , ('f', "pumpsOver_f_Dark") ]-  , poverrideLit = [ ('%', "doorlessMachineryOver_#")-                   , ('f', "pumpsOver_f_Lit") ]-  }-oval = PlaceKind-  { psymbol  = 'o'-  , pname    = "a dome"-  , pfreq    = [ ("rogue", 20000), ("arena", 30000), ("museum", 30000)-               , ("laboratory", 50000), ("empty", 5000), ("exit", 5000)-               , ("ambush", 20000) ]-  , prarity  = [(1, 1)]-  , pcover   = CStretch-  , pfence   = FWall-  , ptopLeft = [ "####·"-               , "##···"-               , "#··tt"-               , "#·t··"-               , "··t··"-               ]-  , poverrideDark = [ ('t', "trailLit"), ('a', "safeTrailLit")-                    , ('~', "poolOver_~_Dark") ]-  , poverrideLit = [ ('t', "trailLit"), ('a', "safeTrailLit")-                   , ('~', "poolOver_~_Lit") ]-  }-ovalFloor = oval-  { pfreq    = [ ("rogue", 150000), ("arena", 60000), ("museum", 60000)-               , ("laboratory", 100000), ("empty", 10000), ("exit", 5000)-               , ("ambush", 100000) ]-  , pfence   = FGround-  , ptopLeft = [ "aXXX##"-               , "X+###·"-               , "X#a···"-               , "X#·a·a"-               , "##··a·"-               , "#··a··"-               ]-  }-ovalSquare = oval-  { pfence   = FGround-  , ptopLeft = [ "X###+"-               , "##···"-               , "#····"-               , "#····"-               , "+····"-               ]-  }-ovalBasin = oval-  { pname    = "a water basin"-  , pfreq    = [ ("rogue", 100000), ("arena", 200000), ("laboratory", 200000)-               , ("empty", 20000) ]-  , pfence   = FGround-  , ptopLeft = [ "XXX+##"-               , "X###··"-               , "X#····"-               , "+#··~~"-               , "#··~~~"-               , "#··~~~"-               ]-  }-ovalBasin2 = oval-  { pname    = "a water basin"-  , pfreq    = [ ("rogue", 600), ("arena", 10000), ("laboratory", 3000)-               , ("empty", 1800) ]-  , pfence   = FWall-  , ptopLeft = [ "#···"-               , "··~~"-               , "·~~~"-               , "·~~~"-               ]-  }-squareBasin = oval-  { pname    = "a water basin"-  , pfreq    = [("arena", 15000), ("laboratory", 3000), ("empty", 4000)]-  , pfence   = FNone-  , ptopLeft = [ "0tt0t"-               , "t~~~~"-               , "t~0~~"-               , "0~~~~"-               , "t~~~~"-               ]-  }-squareBasin2 = oval-  { pname    = "a water basin"-  , pfreq    = [("arena", 100000), ("laboratory", 50000), ("empty", 15000)]-  , pfence   = FNone-  , ptopLeft = [ "0t0ttt"-               , "t~~~~~"-               , "0~~~~~"-               , "t~~0~~"-               , "t~~~~~"-               , "t~~~~~"-               ]-  }-floodedRoom = PlaceKind  -- Valid for any nonempty area, hence low frequency.-  { psymbol  = 'f'-  , pname    = "a flooded room"-  , pfreq    = [("rogue", 10), ("laboratory", 12), ("brawl", 40), ("zoo", 50)]-  , prarity  = [(1, 1)]-  , pcover   = CStretch-  , pfence   = FWall-  , ptopLeft = ["~"]-  , poverrideDark = []-  , poverrideLit = []-  }-maze = PlaceKind-  { psymbol  = 'm'-  , pname    = "an intricate maze"-  , pfreq    = [ ("rogue", 60), ("laboratory", 1500), ("arena", 3)-               , ("museum", 3), ("exit", 300) ]-  , prarity  = [(1, 1)]-  , pcover   = CStretch-  , pfence   = FWall-  , ptopLeft = [ "##··"-               , "#··#"-               , "··#·"-               ]-  , poverrideDark = [ ('&', "cache maze"), ('+', "trapped door")-                    , ('i', "floorActorItem")  -- lit or not, randomly-                    , ('$', "trappableWall") ]-  , poverrideLit = [ ('&', "cache maze"), ('+', "trapped door")-                   , ('i', "floorActorItem")  -- lit or not, randomly-                   , ('$', "trappableWall") ]-  }-maze2 = maze-  { pfreq    = [ ("rogue", 180), ("laboratory", 12000), ("arena", 6)-               , ("museum", 6), ("exit", 360) ]-  , ptopLeft = [ "#·##·"-               , "·#··#"-               , "···#·"-               ]-  }-maze3 = maze-  { pfreq    = [ ("rogue", 300), ("laboratory", 15000), ("arena", 9)-               , ("exit", 400) ]-  , ptopLeft = [ "##·##·"-               , "#·#··#"-               , "~·%···"-               ]-  }-mazeBig = maze-  { pfreq    = [ ("rogue", 600), ("laboratory", 3000), ("arena", 5000)-               , ("exit", 500) ]-  , pfence   = FNone-  , ptopLeft = [ "X$$$$"-               , "$·##·"-               , "$#···"-               , "$#·+%"-               , "$··%i"-               ]-  }-mazeBig2 = maze-  { pfreq    = [ ("rogue", 1500), ("laboratory", 8000), ("arena", 10000)-               , ("exit", 700) ]-  , pfence   = FNone-  , ptopLeft = [ "XX$$$~"-               , "X#···%"-               , "$·###·"-               , "$·+&%%"-               , "$·#iii"-               ]-  }-cells = PlaceKind-  { psymbol  = '#'-  , pname    = "air filters"-  , pfreq    = [ ("rogue", 40), ("laboratory", 48), ("museum", 10)-               , ("exit", 240), ("noise", 480)-               , ("zoo", 700), ("ambush", 80) ]-  , prarity  = [(1, 1)]-  , pcover   = CReflect-  , pfence   = FWall-  , ptopLeft = [ "#··"-               , "·%·"-               , "··#"-               ]-  , poverrideDark = [ ('%', "doorlessMachineryOver_#")-                    , ('f', "bushClumpOver_f_Dark"), ('o', "oilOver_o_Dark") ]-  , poverrideLit = [ ('%', "doorlessMachineryOver_#")-                   , ('f', "bushClumpOver_f_Lit"), ('o', "oilOver_o_Lit") ]-  }-cells2 = cells-  { pname    = "humidity equalizers"-  , prarity  = [(1, 2), (10, 2)]-  , ptopLeft = [ "f·#·"  -- extra column to avoid blocked exits-               , "·#··"-               , "·#··"-               ]-  }-cells3 = cells-  { pname    = "thermostat units"-  , ptopLeft = [ "·^#"-               , "·#~"-               , "··#"-               ]-  }-cells4 = cells-  { pname    = "a power node"-  , ptopLeft = [ "·o#"-               , "o#o"-               , "#o·"-               ]-  }-cells5 = cells  -- this one is distinct enough from others, so needs a boost-  { pname    = "broken robot holds"-  , pfreq    = [ ("rogue", 20), ("laboratory", 15)-               , ("empty", 80), ("exit", 90), ("noise", 150) ]-  , ptopLeft = [ "··#"-               , "··#"-               , "##o"-               ]-  }-cells6 = cells-  { pname    = "animal holding pens"-  , pfreq    = [ ("arena", 2), ("laboratory", 8), ("zoo", 80)]-  , ptopLeft = [ "··#"-               , "##'"-               ]-  }-cells7 = cells-  { pname    = "a defunct control room"-  , pfreq    = [ ("rogue", 5), ("laboratory", 20)-               , ("empty", 100), ("exit", 30), ("noise", 100), ("ambush", 50) ]-  , pfence   = FFloor-  , ptopLeft = [ "%·o"-               , "·#o"-               ]-  }-tank = PlaceKind-  { psymbol  = 'c'-  , pname    = "a tank"-  , pfreq    = [("empty", 1)]-      -- no point taking up space if very little space taken,-      -- but if no other place can be generated, a failsafe is useful-  , prarity  = [(1, 1)]-  , pcover   = CStretch-  , pfence   = FNone-  , ptopLeft = [ "#"-               ]-  , poverrideDark = [('#', "doorlessWallOver_#"), ('r', "reinforced wall")]-  , poverrideLit = [('#', "doorlessWallOver_#"), ('r', "reinforced wall")]-  }-tank2 = tank-  { pfreq    = [ ("empty", 500), ("exit", 15), ("noise", 100)-               , ("battle", 50) ]-  , ptopLeft = [ "0#"-               , "##"-               ]-  }-tank3 = tank-  { pfreq    = [ ("empty", 600), ("exit", 30), ("noise", 200)-               , ("battle", 100) ]-  , ptopLeft = [ "rr#"-               , "r##"-               , "###"-               ]-  }-tank4 = tank-  { pfreq    = [ ("empty", 800), ("exit", 120), ("noise", 300)-               , ("battle", 300) ]-  , ptopLeft = [ "XX0#"-               , "Xrr#"-               , "0r##"-               , "####"-               ]-  }-tank5 = tank-  { pname    = "a cistern"-  , pfreq    = [ ("empty", 1000), ("exit", 150), ("noise", 300)-               , ("battle", 300) ]-  , ptopLeft = [ "XXr#"-               , "Xr##"-               , "r###"-               , "####"-               ]-  }-shuttleHusk = PlaceKind-  { psymbol  = 's'-  , pname    = "a shuttle husk"-  , pfreq    = [("empty", 1000), ("exit", 15000), ("ambush", 15000)]-  , prarity  = [(1, 1)]-  , pcover   = CMirror-  , pfence   = FGround-  , ptopLeft = [ "X·###·X"  -- 7 x 9-               , "X%#w#%X"-               , "#%···%#"-               , "#··h··#"-               , "#w··rw#"-               , "···rr&c"-               , "###&###"-               , "XhhchhX"-               , "hh#w#hh"-               ]-  , poverrideDark = [ ('·', "oily floor Dark")-                    , ('r', "rubbleOrWaste_Dark")-                    , ('#', "shuttle hull")-                    , ('c', "cache shuttle")-                    , ('h', "hardware rack")-                    , ('w', "reinforced wall") ]-  , poverrideLit = [ ('·', "oily floor Lit")-                   , ('r', "rubbleOrWaste_Lit")-                   , ('#', "shuttle hull")-                   , ('c', "cache shuttle")-                   , ('h', "hardware rack")-                   , ('w', "reinforced wall") ]-  }-shuttleHusk2 = shuttleHusk-  { pfreq    = [("empty", 1000), ("exit", 15000), ("ambush", 15000)]-  , ptopLeft = map (T.cons 'X' . flip T.snoc 'X')-               $ ptopLeft shuttleHusk  -- 9 x 9-  }-shuttleHusk3 = shuttleHusk-  { pfreq    = [("empty", 300), ("exit", 5000), ("ambush", 5000)]-  , ptopLeft = [ "X··##··X"  -- 8 x 8-               , "X#%ww%#X"-               , "#w····w#"-               , "····h·r#"-               , "#·rrrrr#"-               , "###&&###"-               , "XhhcchhX"-               , "hh#ww#hh"-               ]-  }-shuttleHusk4 = shuttleHusk3-  { pfreq    = [("empty", 300), ("exit", 5000), ("ambush", 5000)]-  , ptopLeft = map (T.cons 'X' . flip T.snoc 'X')-               $ ptopLeft shuttleHusk3  -- 10 x 8-  }-shuttleHusk5 = shuttleHusk-  { pfreq    = [("empty", 1600), ("exit", 80000), ("ambush", 80000)]-  , pfence   = FGround-  , ptopLeft = [ "···##···"  -- 8 x 10-               , "w#%ww%#w"-               , "X#····#X"-               , "X···h·#X"-               , "#w····w#"-               , "%rr····%"-               , "##rrrr##"-               , "X##&&##X"-               , "XhhcchhX"-               , "hh#ww#hh"-               ]-  }-shuttleHusk6 = shuttleHusk-  { pfreq    = [("empty", 2000), ("exit", 120000), ("ambush", 120000)]-  , ptopLeft = [ "X··###··X"  -- 9 x 10-               , "X#%#w#%#X"-               , "##·h·h·##"-               , "········%"-               , "#w·····w#"-               , "%·····rr%"-               , "##·rrrr##"-               , "X###&###X"-               , "XXhhchhXX"-               , "Xhh#w#hhX"-               ]-  }---- * Helper functions--switchExitToUp :: Text -> PlaceKind -> PlaceKind-switchExitToUp terminal s = s- { psymbol   = '<'- , pname     = pname s <+> "up"- , pfreq     = map (\(t, k) -> (toGroupName $ fromGroupName t <+> "up", k))-               $ pfreq s- , poverrideDark = ('>', toGroupName $ terminal <+> "Dark")-                   : filter ((/= '>') . fst) (poverrideDark s)- , poverrideLit = ('>', toGroupName $ terminal <+> "Lit")-                  : filter ((/= '>') . fst) (poverrideLit s)- }--switchExitToDown :: Text -> PlaceKind -> PlaceKind-switchExitToDown terminal s = s- { psymbol   = '>'- , pname     = pname s <+> "down"- , pfreq     = map (\(t, k) -> (toGroupName $ fromGroupName t <+> "down", k))-               $ pfreq s- , poverrideDark = ('<', toGroupName $ terminal <+> "Dark")-                   : filter ((/= '<') . fst) (poverrideDark s)- , poverrideLit = ('<', toGroupName $ terminal <+> "Lit")-                  : filter ((/= '<') . fst) (poverrideLit s)- }---overrideGatedStaircase :: [(Char, GroupName TileKind)]-overrideGatedStaircase =-  [ ('<', "gated staircase up"), ('>', "gated staircase down")-  , ('I', "signboard"), ('S', "fillerWall") ]--switchStaircaseToGated :: PlaceKind -> PlaceKind-switchStaircaseToGated s = s- { psymbol   = 'g'- , pname     = T.unwords $ "a gated" : tail (T.words (pname s))- , pfreq     = map (first (\t -> toGroupName $ "gated" <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideGatedStaircase- , poverrideLit = overrideGatedStaircase- }--overrideGatedLift :: [(Char, GroupName TileKind)]-overrideGatedLift =-  [ ('<', "gated lift up"), ('>', "gated lift down")-  , ('I', "signboard"), ('S', "lift shaft") ]--switchLiftToGated :: PlaceKind -> PlaceKind-switchLiftToGated s = s- { psymbol   = 'g'- , pname     = T.unwords $ "a gated" : tail (T.words (pname s))- , pfreq     = map (first (\t -> toGroupName $ "gated" <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideGatedLift- , poverrideLit = overrideGatedLift- }---overrideDecontaminatingStaircase :: [(Char, GroupName TileKind)]-overrideDecontaminatingStaircase =-  [ ('<', "decontaminating staircase up")-  , ('>', "decontaminating staircase down")-  , ('I', "signboard"), ('S', "fillerWall") ]--switchStaircaseToDecontaminating :: PlaceKind -> PlaceKind-switchStaircaseToDecontaminating s = s- { psymbol   = 'd'- , pfreq     = map (first (\t -> toGroupName $ "decontaminating"-                                               <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideDecontaminatingStaircase- , poverrideLit = overrideDecontaminatingStaircase- }--overrideDecontaminatingLift :: [(Char, GroupName TileKind)]-overrideDecontaminatingLift =-  [ ('<', "decontaminating lift up")-  , ('>', "decontaminating lift down")-  , ('I', "signboard"), ('S', "lift shaft") ]--switchLiftToDecontaminating :: PlaceKind -> PlaceKind-switchLiftToDecontaminating s = s- { psymbol   = 'd'- , pfreq     = map (first (\t -> toGroupName $ "decontaminating"-                                               <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideDecontaminatingLift- , poverrideLit = overrideDecontaminatingLift- }---overrideWeldedStaircase :: [(Char, GroupName TileKind)]-overrideWeldedStaircase =-  [ ('<', "welded staircase up"), ('>', "ordinary staircase down")-  , ('I', "signboard"), ('S', "fillerWall") ]--switchStaircaseToWelded :: PlaceKind -> PlaceKind-switchStaircaseToWelded s = s- { psymbol   = 'w'- , pfreq     = map (first (\t -> toGroupName $ "welded" <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideWeldedStaircase- , poverrideLit = overrideWeldedStaircase- }--overrideWeldedLift :: [(Char, GroupName TileKind)]-overrideWeldedLift =-  [ ('<', "welded lift up"), ('>', "ordinary lift down")-  , ('I', "signboard"), ('S', "lift shaft") ]--switchLiftToWelded :: PlaceKind -> PlaceKind-switchLiftToWelded s = s- { psymbol   = 'w'- , pfreq     = map (first (\t -> toGroupName $ "welded" <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideWeldedLift- , poverrideLit = overrideWeldedLift- }---overrideOutdoor :: [(Char, GroupName TileKind)]-overrideOutdoor =-  [ ('<', "staircase outdoor up"), ('>', "staircase outdoor down")-  , ('I', "signboard"), ('S', "fillerWall") ]--switchStaircaseToOutdoor :: PlaceKind -> PlaceKind-switchStaircaseToOutdoor s = s- { psymbol   = 'o'- , pname     = "an outdoor area exit"- , pfreq     = map (first (\t -> toGroupName $ "outdoor" <+> fromGroupName t))-               $ pfreq s- , poverrideDark = overrideOutdoor- , poverrideLit = overrideOutdoor- }--switchEscapeToUp :: PlaceKind -> PlaceKind-switchEscapeToUp s = s- { psymbol   = '<'- , pname     = "an escape up"- , pfreq     = map (\(_, n) -> ("escape up", n)) $ pfreq s- , poverrideDark = ('>', "escape up") : poverrideDark s- , poverrideLit = ('>', "escape up") : poverrideLit s- }--switchEscapeToOutdoorDown :: PlaceKind -> PlaceKind-switchEscapeToOutdoorDown s = s- { pname     = "outdoor escape route"- , pfreq     = map (\(_, n) -> ("escape outdoor down", n)) $ pfreq s- , poverrideDark = ('>', "escape outdoor down") : poverrideDark s- , poverrideLit = ('>', "escape outdoor down") : poverrideLit s- }--switchEscapeToSpaceshipDown :: PlaceKind -> PlaceKind-switchEscapeToSpaceshipDown s = s- { pname     = "escape from spaceship"- , pfreq     = map (\(_, n) -> ("escape spaceship down", n)) $ pfreq s- , poverrideDark = ('>', "escape spaceship down") : poverrideDark s- , poverrideLit = ('>', "escape spaceship down") : poverrideLit s+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history)+-- This file is a part of the computer game Allure of the Stars+-- and is released under the terms of the GNU Affero General Public License.+-- For license and copyright information, see the file LICENSE.+--+-- | Definitions of place kinds. Every room in the game is an instantiated+-- place kind.+module Content.PlaceKind+  ( -- * Group name patterns+    pattern ROGUE, pattern LABORATORY, pattern ZOO, pattern BRAWL, pattern SHOOTOUT, pattern ARENA, pattern ESCAPE, pattern AMBUSH, pattern BATTLE, pattern NOISE, pattern EMPTY+  , pattern INDOOR_ESCAPE_DOWN, pattern INDOOR_ESCAPE_UP, pattern OUTDOOR_ESCAPE_DOWN, pattern TINY_STAIRCASE, pattern OPEN_STAIRCASE, pattern CLOSED_STAIRCASE, pattern WALLED_STAIRCASE, pattern GATED_TINY_STAIRCASE, pattern GATED_OPEN_STAIRCASE, pattern GATED_CLOSED_STAIRCASE, pattern OUTDOOR_TINY_STAIRCASE, pattern OUTDOOR_CLOSED_STAIRCASE, pattern OUTDOOR_WALLED_STAIRCASE+  , pattern RESIDENTIAL, pattern MUSEUM, pattern EXIT, pattern RAID+  , pattern TINY_LIFT, pattern OPEN_LIFT, pattern WALLED_LIFT, pattern CLOSED_LIFT, pattern ESCAPE_FROM_SPACESHIP_DOWN, pattern DECON_TINY_STAIRCASE, pattern DECON_OPEN_STAIRCASE, pattern DECON_WALLED_STAIRCASE, pattern DECON_TINY_LIFT, pattern DECON_OPEN_LIFT, pattern DECON_WALLED_LIFT, pattern GATED_TINY_LIFT, pattern GATED_OPEN_LIFT, pattern GATED_CLOSED_LIFT, pattern WELDED_TINY_LIFT, pattern WELDED_OPEN_LIFT, pattern WELDED_WALLED_LIFT, pattern WELDED_TINY_STAIRCASE, pattern WELDED_OPEN_STAIRCASE, pattern WELDED_WALLED_STAIRCASE+  , groupNamesSingleton, groupNames+  , -- * Content+    content+  ) where++import Prelude ()++import Game.LambdaHack.Core.Prelude++import qualified Data.Text as T++import Content.TileKind hiding (content, groupNames, groupNamesSingleton)+import Game.LambdaHack.Content.PlaceKind+import Game.LambdaHack.Content.TileKind (TileKind)+import Game.LambdaHack.Definition.Defs++-- * Group name patterns++groupNamesSingleton :: [GroupName PlaceKind]+groupNamesSingleton = []++-- TODO: if we stick to the current system of generating extra kinds and their+-- group names, let's also add the generated group names to @groupNames@.+groupNames :: [GroupName PlaceKind]+groupNames =+       [ROGUE, LABORATORY, ZOO, BRAWL, SHOOTOUT, ARENA, ESCAPE, AMBUSH, BATTLE, NOISE, EMPTY]+    ++ [INDOOR_ESCAPE_DOWN, INDOOR_ESCAPE_UP, OUTDOOR_ESCAPE_DOWN, TINY_STAIRCASE, OPEN_STAIRCASE, CLOSED_STAIRCASE, WALLED_STAIRCASE]+    ++ [RESIDENTIAL, MUSEUM, EXIT, RAID]+    ++ [TINY_LIFT, OPEN_LIFT, WALLED_LIFT, CLOSED_LIFT, ESCAPE_FROM_SPACESHIP_DOWN]+    ++ fst generatedStairs++pattern ROGUE, LABORATORY, ZOO, BRAWL, SHOOTOUT, ARENA, ESCAPE, AMBUSH, BATTLE, NOISE, EMPTY :: GroupName PlaceKind++pattern INDOOR_ESCAPE_DOWN, INDOOR_ESCAPE_UP, OUTDOOR_ESCAPE_DOWN, TINY_STAIRCASE, OPEN_STAIRCASE, CLOSED_STAIRCASE, WALLED_STAIRCASE, GATED_TINY_STAIRCASE, GATED_OPEN_STAIRCASE, GATED_CLOSED_STAIRCASE, OUTDOOR_TINY_STAIRCASE, OUTDOOR_CLOSED_STAIRCASE, OUTDOOR_WALLED_STAIRCASE :: GroupName PlaceKind++pattern RESIDENTIAL, MUSEUM, EXIT, RAID :: GroupName PlaceKind++pattern TINY_LIFT, OPEN_LIFT, WALLED_LIFT, CLOSED_LIFT, ESCAPE_FROM_SPACESHIP_DOWN, DECON_TINY_STAIRCASE, DECON_OPEN_STAIRCASE, DECON_WALLED_STAIRCASE, DECON_TINY_LIFT, DECON_OPEN_LIFT, DECON_WALLED_LIFT, GATED_TINY_LIFT, GATED_OPEN_LIFT, GATED_CLOSED_LIFT, WELDED_TINY_LIFT, WELDED_OPEN_LIFT, WELDED_WALLED_LIFT, WELDED_TINY_STAIRCASE, WELDED_OPEN_STAIRCASE, WELDED_WALLED_STAIRCASE :: GroupName PlaceKind++pattern ROGUE = GroupName "rogue"+pattern LABORATORY = GroupName "laboratory"+pattern ZOO = GroupName "zoo"+pattern BRAWL = GroupName "brawl"+pattern SHOOTOUT = GroupName "shootout"+pattern ARENA = GroupName "arena"+pattern ESCAPE = GroupName "escape"+pattern AMBUSH = GroupName "ambush"+pattern BATTLE = GroupName "battle"+pattern NOISE = GroupName "noise"+pattern EMPTY = GroupName "empty"++pattern INDOOR_ESCAPE_DOWN = GroupName "escape down"+pattern INDOOR_ESCAPE_UP = GroupName "escape up"+pattern OUTDOOR_ESCAPE_DOWN = GroupName "outdoor escape route"+pattern TINY_STAIRCASE = GroupName "tiny staircase"+pattern OPEN_STAIRCASE = GroupName "open staircase"+pattern CLOSED_STAIRCASE = GroupName "closed staircase"+pattern WALLED_STAIRCASE = GroupName "walled staircase"++-- This is a rotten compromise, because these are synthesized below,+-- so typos can happen.+pattern GATED_TINY_STAIRCASE = GroupName "gated tiny staircase"+pattern GATED_OPEN_STAIRCASE = GroupName "gated open staircase"+pattern GATED_CLOSED_STAIRCASE = GroupName "gated closed staircase"+pattern OUTDOOR_TINY_STAIRCASE = GroupName "outdoor tiny staircase"+pattern OUTDOOR_CLOSED_STAIRCASE = GroupName "outdoor closed staircase"+pattern OUTDOOR_WALLED_STAIRCASE = GroupName "outdoor walled staircase"++-- ** Allure-specific+pattern RESIDENTIAL = GroupName "residential"+pattern MUSEUM = GroupName "museum"+pattern EXIT = GroupName "exit"+pattern RAID = GroupName "raid"++pattern TINY_LIFT = GroupName "tiny lift"+pattern OPEN_LIFT = GroupName "open lift"+pattern WALLED_LIFT = GroupName "walled lift"+pattern CLOSED_LIFT = GroupName "closed lift"+pattern ESCAPE_FROM_SPACESHIP_DOWN = GroupName "escape from spaceship"++-- This is a rotten compromise, because these are synthesized below,+-- so typos can happen.+pattern DECON_TINY_STAIRCASE = GroupName "decon tiny staircase"+pattern DECON_OPEN_STAIRCASE = GroupName "decon open staircase"+pattern DECON_WALLED_STAIRCASE = GroupName "decon walled staircase"+pattern DECON_TINY_LIFT = GroupName "decon tiny lift"+pattern DECON_OPEN_LIFT = GroupName "decon open lift"+pattern DECON_WALLED_LIFT = GroupName "decon walled lift"+pattern GATED_TINY_LIFT = GroupName "gated tiny lift"+pattern GATED_OPEN_LIFT = GroupName "gated open lift"+pattern GATED_CLOSED_LIFT = GroupName "gated closed lift"+pattern WELDED_TINY_LIFT = GroupName "welded tiny lift"+pattern WELDED_OPEN_LIFT = GroupName "welded open lift"+pattern WELDED_WALLED_LIFT = GroupName "welded walled lift"+pattern WELDED_TINY_STAIRCASE = GroupName "welded tiny staircase"+pattern WELDED_OPEN_STAIRCASE = GroupName "welded open staircase"+pattern WELDED_WALLED_STAIRCASE = GroupName "welded walled staircase"++-- * Content++content :: [PlaceKind]+content =+  [deadEnd, rect, rect2, rectWindows, glasshouse, glasshouse2, glasshouse3, glasshouse4, pulpit, ruin, ruin2, collapsed, collapsed2, collapsed3, collapsed4, collapsed5, collapsed6, collapsed7, pillar, pillar2, pillar3, pillar4, pillar5, pillar6, colonnade, colonnade2, colonnade3, colonnade4, colonnade5, colonnade6, colonnade7, colonnade8, colonnade9, colonnade10, lampPost, lampPost2, lampPost3, lampPost4, treeShade, fogClump, fogClump2, smokeClump, smokeClump2, smokeClump3FGround, bushClump, escapeDown, escapeDown2, escapeDown3, escapeDown4, escapeDown5, escapeDown6, escapeDown7, escapeDown8, escapeDown9, staircase1, staircase2, staircase3, staircase4, staircase5, staircase6, staircase7, staircase8, staircase9, staircase10, staircase11, staircase12, staircase13, staircase14, staircase15, staircase16, staircase17, staircase18, staircase19, staircase20, staircase21, staircase22, staircase23, staircase24, staircase25, staircase26, staircase27, staircase28, staircase29, staircase30, staircase31, staircase32, staircase33, staircase34, staircase35, staircase36, staircase37]+  -- Allure-specific+  ++ [staircaseLift11, staircaseLift12, staircaseLift13, staircaseLift14, staircaseLift15, staircaseLift16, staircaseLift17, staircaseLift18, staircaseLift19, staircaseLift20, staircaseLift21, staircaseLift22, staircaseLift23, staircaseLift24, staircaseLift25]+  -- automatically generated+  ++ snd generatedStairs ++ generatedEscapes+  -- Allure-specific, continued+  ++ [ pumps, oval, ovalFloor, ovalSquare, ovalBasin, ovalBasin2, squareBasin, squareBasin2, floodedRoom, floodedRoom2, maze, maze2, maze3, maze4, mazeBig, mazeBig2, cells, cells2, cells3, cells4, cells5, cells6, cells7, tank, tank2, tank3, tank4, tank5, tank6, tank7, tank8, tank9, tank10, tank11, tank12, shuttleHusk, shuttleHusk2, shuttleHusk3, shuttleHusk4, shuttleHusk5, shuttleHusk6, dormitory, dormitory2, dormitory3, dormitory4, dormitory5, dormitory6]++deadEnd,    rect, rect2, rectWindows, glasshouse, glasshouse2, glasshouse3, glasshouse4, pulpit, ruin, ruin2, collapsed, collapsed2, collapsed3, collapsed4, collapsed5, collapsed6, collapsed7, pillar, pillar2, pillar3, pillar4, pillar5, pillar6, colonnade, colonnade2, colonnade3, colonnade4, colonnade5, colonnade6, colonnade7, colonnade8, colonnade9, colonnade10, lampPost, lampPost2, lampPost3, lampPost4, treeShade, fogClump, fogClump2, smokeClump, smokeClump2, smokeClump3FGround, bushClump, escapeDown, escapeDown2, escapeDown3, escapeDown4, escapeDown5, escapeDown6, escapeDown7, escapeDown8, escapeDown9, staircase1, staircase2, staircase3, staircase4, staircase5, staircase6, staircase7, staircase8, staircase9, staircase10, staircase11, staircase12, staircase13, staircase14, staircase15, staircase16, staircase17, staircase18, staircase19, staircase20, staircase21, staircase22, staircase23, staircase24, staircase25, staircase26, staircase27, staircase28, staircase29, staircase30, staircase31, staircase32, staircase33, staircase34, staircase35, staircase36, staircase37 :: PlaceKind+-- Allure-specific+staircaseLift11, staircaseLift12, staircaseLift13, staircaseLift14, staircaseLift15, staircaseLift16, staircaseLift17, staircaseLift18, staircaseLift19, staircaseLift20, staircaseLift21, staircaseLift22, staircaseLift23, staircaseLift24, staircaseLift25, pumps, oval, ovalFloor, ovalSquare, ovalBasin, ovalBasin2, squareBasin, squareBasin2, floodedRoom, floodedRoom2, maze, maze2, maze3, maze4, mazeBig, mazeBig2, cells, cells2, cells3, cells4, cells5, cells6, cells7, tank, tank2, tank3, tank4, tank5, tank6, tank7, tank8, tank9, tank10, tank11, tank12, shuttleHusk, shuttleHusk2, shuttleHusk3, shuttleHusk4, shuttleHusk5, shuttleHusk6, dormitory, dormitory2, dormitory3, dormitory4, dormitory5, dormitory6 :: PlaceKind++staircase, staircaseLift :: PlaceKind  -- templates++staircaseBasic :: [PlaceKind]+staircaseBasic = [staircase1, staircase2, staircase3, staircase4, staircase5, staircase6, staircase7, staircase8, staircase9, staircase10, staircase11, staircase12, staircase13, staircase14, staircase15, staircase16, staircase17, staircase18, staircase19, staircase20, staircase21, staircase22, staircase23, staircase24, staircase25, staircase26, staircase27, staircase28, staircase29, staircase30, staircase31, staircase32, staircase33, staircase34, staircase35, staircase36, staircase37]+  -- Allure-specific+  ++ [staircaseLift11, staircaseLift12, staircaseLift13, staircaseLift14, staircaseLift15, staircaseLift16, staircaseLift17, staircaseLift18, staircaseLift19, staircaseLift20, staircaseLift21, staircaseLift22, staircaseLift23, staircaseLift24, staircaseLift25]++generatedStairs :: ([GroupName PlaceKind], [PlaceKind])+generatedStairs =+  let (stairs, lifts) = partition ((/= "a lift") . pname) staircaseBasic+      gatedStairs = map switchStaircaseToGated stairs+      gatedLifts = map switchLiftToGated lifts+      deconStairs = map switchStaircaseToDecon stairs+      deconLifts = map switchLiftToDecon lifts+      weldedStairs = map switchStaircaseToWelded stairs+      weldedLifts = map switchLiftToWelded lifts+      outdoorStairs = map switchStaircaseToOutdoor stairs+      stairsAll = stairs ++ gatedStairs ++ deconStairs ++ weldedStairs+                  ++ outdoorStairs+      liftsAll = lifts ++ gatedLifts ++ deconLifts ++ weldedLifts+      genStairs =+        gatedStairs ++ gatedLifts+        ++ deconStairs ++ deconLifts+        ++ weldedStairs ++ weldedLifts+        ++ outdoorStairs+        ++ map (switchExitToUp "stair terminal") stairsAll+        ++ map (switchExitToUp "lift terminal") liftsAll+        ++ map (switchExitToDown "stair terminal") stairsAll+        ++ map (switchExitToDown "lift terminal") liftsAll+  in ( nub $ sort $ concatMap (map fst . pfreq) genStairs+     , genStairs )++escapeDownBasic :: [PlaceKind]+escapeDownBasic =+  [ escapeDown, escapeDown2, escapeDown3, escapeDown4, escapeDown5, escapeDown6+  , escapeDown7, escapeDown8, escapeDown9 ]++generatedEscapes :: [PlaceKind]+generatedEscapes =+  let upEscapes = map switchEscapeToUp escapeDownBasic+      outdoorEscapes = map switchEscapeToOutdoorDown escapeDownBasic+      spaceshipEscapes = map switchEscapeToSpaceshipDown escapeDownBasic+  in upEscapes ++ outdoorEscapes ++ spaceshipEscapes++-- The dots below are @'\x00B7'@, as defined in @TileKind.floorSymbol@.+deadEnd = PlaceKind  -- needs to have index 0+  { psymbol  = 'd'+  , pname    = "a dead end"+  , pfreq    = []+  , prarity  = []+  , pcover   = CStretch+  , pfence   = FNone+  , ptopLeft = ["·"]+  , poverrideDark = []+  , poverrideLit = []+  }+rect = PlaceKind  -- Valid for any nonempty area, hence low frequency.+  { psymbol  = 'r'+  , pname    = "a room"+  , pfreq    = [(ROGUE, 100), (LABORATORY, 10), (RAID, 100)]+  , prarity  = [(1, 10), (10, 6)]+  , pcover   = CStretch+  , pfence   = FWall+  , ptopLeft = ["·"]+  , poverrideDark = []+  , poverrideLit = []+  }+rect2 = rect+  { pname    = "a pen"+  , pfreq    = [(SHOOTOUT, 1), (ZOO, 10)]+  }+rectWindows = PlaceKind+  { psymbol  = 'w'+  , pname    = "a shed"+  , pfreq    = [(ESCAPE, 20)]+  , prarity  = [(1, 10), (10, 10)]+  , pcover   = CStretch+  , pfence   = FNone+  , ptopLeft = [ "#%"+               , "%·"+               ]+  , poverrideDark = [('%', RECT_WINDOWS)]+  , poverrideLit = [('%', RECT_WINDOWS)]+  }+glasshouse = PlaceKind+  { psymbol  = 'g'+  , pname    = "a glasshouse"+  , pfreq    = [(SHOOTOUT, 10)]+  , prarity  = [(1, 10), (10, 7)]+  , pcover   = CStretch+  , pfence   = FNone+  , ptopLeft = [ "%%"+               , "%·"+               ]+  , poverrideDark = []+  , poverrideLit = []+  }+glasshouse2 = glasshouse+  { pname    = "a glass cage"+  , pfreq    = [(LABORATORY, 2), (ZOO, 30)]+  , poverrideDark = [('·', DAMP_FLOOR_DARK)]+  , poverrideLit = [('·', DAMP_FLOOR_LIT)]+  }+glasshouse3 = glasshouse+  { pname    = "an entertainment center"+  , pfreq    = [(ARENA, 1), (AMBUSH, 10)]+  }+glasshouse4 = glasshouse+  { pname    = "an exhibition area"+  , pfreq    = [(ARENA, 1), (MUSEUM, 1)]+  }+pulpit = PlaceKind+  { psymbol  = 'p'+  , pname    = "a stand podium"+  , pfreq    = [(ARENA, 15), (MUSEUM, 15), (ZOO, 100)]+  , prarity  = [(1, 1)]+  , pcover   = CMirror+  , pfence   = FGround+  , ptopLeft = [ "%%·"+               , "%··"+               , "··0"+               ]+  , poverrideDark = [('0', S_PULPIT)]+  , poverrideLit = [('0', S_PULPIT)]+      -- except for floor, this will all be lit, regardless of night/dark; OK+  }+ruin = PlaceKind+  { psymbol  = 'R'+  , pname    = "ruins"+  , pfreq    = [(BATTLE, 660), (AMBUSH, 70)]+  , prarity  = [(1, 1)]+  , pcover   = CStretch+  , pfence   = FWall+  , ptopLeft = ["X"]+  , poverrideDark = [('·', DAMP_FLOOR_DARK)]+  , poverrideLit = [('·', DAMP_FLOOR_LIT)]+  }+ruin2 = ruin+  { pname    = "a scaffolding"+  , pfreq    = [(NOISE, 2000), (EXIT, 5), (MUSEUM, 1)]+  }+collapsed = PlaceKind+  { psymbol  = 'c'+  , pname    = "a hardware stack"+  , pfreq    = [(NOISE, 1)]+      -- no point taking up space if very little space taken,+      -- but if no other place can be generated, a failsafe is useful+  , prarity  = [(1, 1)]+  , pcover   = CStretch+  , pfence   = FNone+  , ptopLeft = [ "#"+               ]+  , poverrideDark = [('#', DOORLESS_MACHINERY)]+  , poverrideLit = [('#', DOORLESS_MACHINERY)]+  }+collapsed2 = collapsed+  { pfreq    = [(NOISE, 1000), (BATTLE, 200)]+  , ptopLeft = [ "X#"+               , "##"+               ]+  }+collapsed3 = collapsed+  { pfreq    = [(NOISE, 2000), (BATTLE, 200)]+  , ptopLeft = [ "XX#"+               , "###"+               ]+  }+collapsed4 = collapsed+  { pfreq    = [(NOISE, 2200), (BATTLE, 200)]+  , ptopLeft = [ "XXX#"+               , "####"+               ]+  }+collapsed5 = collapsed+  { pfreq    = [(NOISE, 3000), (BATTLE, 500)]+  , ptopLeft = [ "XX#"+               , "X##"+               , "###"+               ]+  }+collapsed6 = collapsed+  { pfreq    = [(NOISE, 4000), (BATTLE, 1000)]+  , ptopLeft = [ "XXX#"+               , "X###"+               , "####"+               ]+  }+collapsed7 = collapsed+  { pfreq    = [(NOISE, 4000), (BATTLE, 1000)]+  , ptopLeft = [ "XXX#"+               , "XX##"+               , "####"+               ]+  }+pillar = PlaceKind+  { psymbol  = 'p'+  , pname    = "a market"+  , pfreq    = [(ROGUE, 300), (ARENA, 10000), (EMPTY, 400)]+  , prarity  = [(1, 1)]+  , pcover   = CStretch+  , pfence   = FWall+  -- Larger rooms require support pillars.+  , ptopLeft = [ "····"+               , "·0··"+               , "····"+               , "····"+               ]+  , poverrideDark = [('·', OILY_FLOOR_DARK)]+  , poverrideLit = [('·', OILY_FLOOR_LIT)]+  }+pillar2 = pillar+  { pname    = "a mall"+  , pfreq    = [(ROGUE, 10000), (ARENA, 100000), (EMPTY, 4000)]+  , ptopLeft = [ "0····"+               , "·····"+               , "·····"+               , "···0·"+               , "····~"+               ]+  , poverrideDark = [('~', S_POOL_DARK)]+  , poverrideLit = [('~', S_POOL_LIT)]+  }+pillar3 = pillar+  { pname    = "a court"+  , pfreq    = [ (ROGUE, 250), (ARENA, 15), (MUSEUM, 10)+               , (LABORATORY, 200), (RAID, 50) ]+  , ptopLeft = [ "#··"+               , "···"+               , "···"+               ]+  }+pillar4 = pillar+  { pname    = "a plaza"+  , pfreq    = [ (ROGUE, 1500), (ARENA, 5000)+               , (MUSEUM, 4000), (LABORATORY, 1500) ]+  , ptopLeft = [ "#·#·"+               , "····"+               , "#···"+               , "····"+               ]+  }+pillar5 = pillar+  { pname    = "a bank outlet"+  , pfreq    = [ (ROGUE, 1200), (ARENA, 6000)+               , (EMPTY, 600), (EXIT, 600) ]+  , ptopLeft = [ "&i%·"+               , "ii#·"+               , "%#p·"+               , "····"+               ]+  , poverrideDark = [ ('&', CACHE_DEPOSIT), ('p', TRAPPED_DOOR)+                    , ('i', FLOOR_ACTOR_ITEM) ]  -- lit or not, randomly+  , poverrideLit = [ ('&', CACHE_DEPOSIT), ('p', TRAPPED_DOOR)+                   , ('i', FLOOR_ACTOR_ITEM) ]  -- lit or not, randomly+      -- no STUCK_DOOR, because FWall, so would break global pathfinding+  }+pillar6 = pillar+  { pname    = "a jewelry store"+  , pfreq    = [ (ROGUE, 1200), (ARENA, 6000)+               , (MUSEUM, 7000), (EMPTY, 600) ]+  , ptopLeft = [ "0a··"+               , "aaf·"+               , "·f&·"+               , "····"+               ]+  , poverrideDark = [ ('&', CACHE_JEWELRY), ('0', S_LAMP_POST)+                    , ('f', BUSH_GROVE_DARK) , ('a', S_FLOOR_ACTOR_LIT) ]+  , poverrideLit = [ ('&', CACHE_JEWELRY), ('0', S_LAMP_POST)+                   , ('f', BUSH_GROVE_LIT), ('a', S_FLOOR_ACTOR_LIT) ]+  }+colonnade = PlaceKind+  { psymbol  = 'c'+  , pname    = "a colonnade"+  , pfreq    = [ (ROGUE, 12), (NOISE, 1000), (ESCAPE, 200)+               , (EXIT, 30), (RAID, 12) ]+  , prarity  = [(1, 12), (10, 12)]+  , pcover   = CAlternate+  , pfence   = FFloor+  , ptopLeft = [ "#·"+               , "··"+               ]+  , poverrideDark = []+  , poverrideLit = []+  }+colonnade2 = colonnade+  { pfreq    = [(ROGUE, 300)]+  , prarity  = [(1, 1)]+  , pfence   = FWall+  , ptopLeft = [ "#·"+               , "·#"+               ]+  }+colonnade3 = colonnade+  { prarity  = [(1, 120), (10, 120)]+  , ptopLeft = [ "··#"+               , "·#·"+               , "#··"+               ]+  }+colonnade4 = colonnade+  { prarity  = [(1, 1)]+  , pfreq    = [(ROGUE, 1000), (RAID, 1000)]+  , pfence   = FWall+  , ptopLeft = [ "#··"+               , "·#·"+               , "··#"+               ]+  }+colonnade5 = colonnade+  { prarity  = [(1, 25), (10, 25)]+  , ptopLeft = [ "#··"+               , "··#"+               ]+  }+colonnade6 = colonnade+  { prarity  = [(1, 14), (10, 14)]+  , ptopLeft = [ "#·"+               , "··"+               , "·#"+               ]+  }+colonnade7 = colonnade+  { pfreq    = [(ARENA, 50), (MUSEUM, 30), (EMPTY, 800), (RAID, 40)]+  , prarity  = [(1, 7), (10, 7)]+  , ptopLeft = [ "0·"+               , "··"+               ]+  }+colonnade8 = colonnade7+  { prarity  = [(1, 50), (10, 50)]+  , ptopLeft = [ "··0"+               , "·0·"+               , "0··"+               ]+  }+colonnade9 = colonnade7+  { prarity  = [(1, 20), (10, 20)]+  , ptopLeft = [ "0··"+               , "··0"+               ]+  }+colonnade10 = colonnade7+  { prarity  = [(1, 10), (10, 10)]+  , ptopLeft = [ "0·"+               , "··"+               , "·0"+               ]+  }+lampPost = PlaceKind+  { psymbol  = 'l'+  , pname    = "a lamp-lit area"+  , pfreq    = [ (ESCAPE, 200), (ZOO, 100), (AMBUSH, 1000)+               , (BATTLE, 100) ]+  , prarity  = [(1, 1)]+  , pcover   = CVerbatim+  , pfence   = FNone+  , ptopLeft = [ "X·X"+               , "·0·"+               , "X·X"+               ]+  , poverrideDark = [('0', S_LAMP_POST), ('·', S_FLOOR_ACTOR_LIT)]+  , poverrideLit = [('0', S_LAMP_POST), ('·', S_FLOOR_ACTOR_LIT)]+  }+lampPost2 = lampPost+  { ptopLeft = [ "···"+               , "·0·"+               , "···"+               ]+  }+lampPost3 = lampPost+  { pfreq    = [(ESCAPE, 3000), (ZOO, 500), (BATTLE, 1100)]+  , ptopLeft = [ "XX·XX"+               , "X···X"+               , "··0··"+               , "X···X"+               , "XX·XX"+               ]+  }+lampPost4 = lampPost+  { pfreq    = [(ESCAPE, 3000), (ZOO, 500), (BATTLE, 600)]+  , ptopLeft = [ "X···X"+               , "·····"+               , "··0··"+               , "·····"+               , "X···X"+               ]+  }+treeShade = PlaceKind+  { psymbol  = 't'+  , pname    = "a tree shade"+  , pfreq    = [(BRAWL, 500)]+  , prarity  = [(1, 1)]+  , pcover   = CMirror+  , pfence   = FNone+  , ptopLeft = [ "··s"+               , "s0·"+               , "Xs·"+               ]+  , poverrideDark = [ ('0', S_TREE_DARK)+                    , ('s', TREE_SHADE_WALKABLE_DARK)+                    , ('·', S_SHADED_GROUND) ]+  , poverrideLit = [ ('0', S_TREE_LIT)+                   , ('s', TREE_SHADE_WALKABLE_LIT)+                   , ('·', S_SHADED_GROUND) ]+  }+fogClump = PlaceKind+  { psymbol  = 'f'+  , pname    = "a foggy patch"+  , pfreq    = [(EMPTY, 400), (SHOOTOUT, 70), (ESCAPE, 60), (RAID, 50)]+  , prarity  = [(1, 1)]+  , pcover   = CMirror+  , pfence   = FNone+  , ptopLeft = [ "f;"+               , ";f"+               , ";X"+               ]+  , poverrideDark = [('f', FOG_CLUMP_DARK), (';', S_FOG_LIT)]+  , poverrideLit = [('f', FOG_CLUMP_LIT), (';', S_FOG_LIT)]+  }+fogClump2 = fogClump+  { pfreq    = [(EMPTY, 2200), (SHOOTOUT, 400), (ESCAPE, 100), (RAID, 250)]+  , ptopLeft = [ "X;f"+               , "f;f"+               , ";;f"+               , "Xff"+               ]+  }+smokeClump = PlaceKind+  { psymbol  = 's'+  , pname    = "a smoky patch"+  , pfreq    = [(EXIT, 20), (ZOO, 40), (AMBUSH, 50)]+  , prarity  = [(1, 1)]+  , pcover   = CMirror+  , pfence   = FNone+  , ptopLeft = [ "f;"+               , ";f"+               , ";X"+               ]+  , poverrideDark = [ ('f', SMOKE_CLUMP_DARK), (';', S_SMOKE_LIT)+                    , ('·', S_FLOOR_ACTOR_DARK) ]+  , poverrideLit = [ ('f', SMOKE_CLUMP_LIT), (';', S_SMOKE_LIT)+                   , ('·', S_FLOOR_ACTOR_LIT) ]+  }+smokeClump2 = smokeClump+  { pfreq    = [(EXIT, 100), (ZOO, 200), (AMBUSH, 150)]+  , ptopLeft = [ "X;f"+               , "f;f"+               , ";;f"+               , "Xff"+               ]+  }+smokeClump3FGround = smokeClump+  { pname    = "a burned out area"+  , pfreq    = [(LABORATORY, 50)]  -- close to guaranteed, but not overcrowded+  , prarity  = [(1, 1)]+  , pcover   = CMirror+  , pfence   = FGround+  , ptopLeft = [ ";f:"  -- workshop terrain+               , "f·f"+               , "f·f"+               , ";f;"+               ]+      -- should not be used in caves with trails, because bushes should+      -- not grow over such artificial trails+  }+bushClump = PlaceKind+  { psymbol  = 'b'+  , pname    = "a bushy patch"+  , pfreq    = [(SHOOTOUT, 120), (EMPTY, 60), (BRAWL, 30)]+  , prarity  = [(1, 1)]+  , pcover   = CMirror+  , pfence   = FNone+  , ptopLeft = [ "f;X"  -- the third column is needed to prevent blockage+               , ";Xf"+               , ";fX"+               ]+  , poverrideDark = [('f', BUSH_CLUMP_DARK), (';', S_BUSH_LIT)]+  , poverrideLit = [('f', BUSH_CLUMP_LIT), (';', S_BUSH_LIT)]+      -- should not be used in caves with trails, because bushes can't+      -- grow over such artificial trails+  }+escapeDown = PlaceKind+  { psymbol  = '>'+  , pname    = "an escape down"+  , pfreq    = [(INDOOR_ESCAPE_DOWN, 1)]+  , prarity  = [(1, 1)]+  , pcover   = CVerbatim+  , pfence   = FGround+  , ptopLeft = [ ">"+               ]+  , poverrideDark = [ ('g', S_FROZEN_PATH) , ('0', S_LAMP_POST), ('b', BARREL)+                    , ('a', S_FLOOR_ACTOR_LIT), ('r', RUBBLE_OR_WASTE_DARK) ]+  , poverrideLit = [ ('g', S_FROZEN_PATH), ('0', S_LAMP_POST), ('b', BARREL)+                   , ('a', S_FLOOR_ACTOR_LIT), ('r', RUBBLE_OR_WASTE_LIT) ]+  }+escapeDown2 = escapeDown+  { pfreq    = [(INDOOR_ESCAPE_DOWN, 200)]+  , pfence   = FGround+  , ptopLeft = [ "#·#"+               , "·>·"+               , "#·#"+               ]+  }+escapeDown3 = escapeDown+  { pfreq    = [(INDOOR_ESCAPE_DOWN, 200)]+  , pfence   = FFloor+  , ptopLeft = [ "·b·"+               , "b>b"+               , "·b·"+               ]+  }+escapeDown4 = escapeDown+  { pfreq    = [(INDOOR_ESCAPE_DOWN, 200)]+  , pfence   = FWall+  , ptopLeft = [ "^·^"+               , "·>·"+               , "^·^"+               ]+  }+escapeDown5 = escapeDown+  { pfreq    = [(INDOOR_ESCAPE_DOWN, 200)]+  , pcover   = CMirror+  , pfence   = FFloor+  , ptopLeft = [ "r#·"+               , "r>#"+               , "rrr"+               ]+  }+escapeDown6 = escapeDown+  { pfreq    = [(INDOOR_ESCAPE_DOWN, 1000)]+  , pfence   = FWall+  , ptopLeft = [ "··#··"+               , "·#g#·"+               , "#g>g#"+               , "·#g#·"+               , "··#··"+               ]+  }+escapeDown7 = escapeDown+  { pfreq    = [(INDOOR_ESCAPE_DOWN, 1000)]+  , pfence   = FFloor+  , ptopLeft = [ "·g#g·"+               , "g#g#g"+               , "#g>g#"+               , "g#g#g"+               , "·g#g·"+               ]+  }+escapeDown8 = escapeDown+  { pfreq    = [(INDOOR_ESCAPE_DOWN, 1000)]+  , pcover   = CMirror+  , pfence   = FWall+  , ptopLeft = [ "··#g·"+               , "·#gg·"+               , "·#>g#"+               , "·gg#·"+               , "g·#··"+               ]+  }+escapeDown9 = escapeDown+  { pfreq    = [(INDOOR_ESCAPE_DOWN, 1000)]+  , pcover   = CMirror+  , pfence   = FFloor+  , ptopLeft = [ "·a·#"+               , "%a>·"+               , "%0a·"+               , "aa%%"+               ]+  }+staircase = PlaceKind+  { psymbol  = '/'+  , pname    = "a staircase"+  , pfreq    = [(TINY_STAIRCASE, 1)]  -- no cover when arriving; low freq+  , prarity  = [(1, 100), (10, 100)]+  , pcover   = CVerbatim+  , pfence   = FGround+  , ptopLeft = [ "<S>"+               ]+  , poverrideDark = [ ('<', STAIRCASE_UP), ('>', STAIRCASE_DOWN)+                    , ('I', SIGNBOARD), ('S', FILLER_WALL) ]+  , poverrideLit = [ ('<', STAIRCASE_UP), ('>', STAIRCASE_DOWN)+                   , ('I', SIGNBOARD), ('S', FILLER_WALL) ]+  }+staircase1 = staircase+  { prarity  = [(1, 1)]  -- no cover when arriving; so low rarity+  }+staircase2 = staircase+  { pfreq    = [(TINY_STAIRCASE, 3)]+  , prarity  = [(1, 1)]+  , pfence   = FFloor+  , ptopLeft = [ "·<S>·"+               ]+  }+-- Allure-specific:+staircaseLift = PlaceKind+  { psymbol  = '|'+  , pname    = "a lift"+  , pfreq    = [(TINY_LIFT, 1)]+  , prarity  = [(1, 100), (10, 100)]+  , pcover   = CVerbatim+  , pfence   = FGround+  , ptopLeft = [ "<S>"+               ]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircase3 = staircaseLift+  { prarity  = [(1, 1)]+  }+staircase4 = staircaseLift+  { pfreq    = [(TINY_LIFT, 3)]+  , prarity  = [(1, 1)]+  , ptopLeft = [ "·<S>·"+               ]+  }+staircase5 = staircase+  { pfreq    = [(OPEN_STAIRCASE, 200)]  -- no cover, open+  , pfence   = FGround+  , ptopLeft = [ "#·#"+               , "···"+               , "<S>"+               , "···"+               , "#·#"+               ]+  }+staircase6 = staircaseLift+  { pfreq    = [(OPEN_LIFT, 300)]+  , pfence   = FFloor+  , ptopLeft = [ "#·#·#"+               , "·····"+               , "·<S>·"+               , "·····"+               , "#·#·#"+               ]+  }+staircase7 = staircase+  { pfreq    = [(OPEN_STAIRCASE, 500)]+  , pfence   = FGround+  , ptopLeft = [ "#·#·#·#"+               , "·······"+               , "#·<S>·#"+               , "·······"+               , "#·#·#·#"+               ]+  }+staircase8 = staircaseLift+  { pfreq    = [(OPEN_LIFT, 2000)]+  , pfence   = FFloor+  , ptopLeft = [ "·#·#·#·"+               , "#·····#"+               , "··<S>··"+               , "#·····#"+               , "·#·#·#·"+               ]+  }+staircase9 = staircase+  { pfreq    = [(OPEN_STAIRCASE, 500)]+  , pfence   = FFloor+  , ptopLeft = [ "#·······#"+               , "···<S>···"+               , "#·······#"+               ]+  }+staircase10 = staircaseLift+  { pfreq    = [(OPEN_LIFT, 500)]+  , pfence   = FGround+  , ptopLeft = [ "0·····0"+               , "··<S>··"+               , "0·····0"+               ]+  }+staircase11 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 2000)]  -- weak cover, low freq+  , pfence   = FFloor+  , ptopLeft = [ "·#·"+               , "#·#"+               , "···"+               , "<S>"+               , "···"+               , "#·#"+               , "·#·"+               ]+  }+staircase12 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 4000)]+  , pfence   = FFloor+  , ptopLeft = [ "·#·#·"+               , "#·#·#"+               , "·····"+               , "·<S>·"+               , "·····"+               , "#·#·#"+               , "·#·#·"+               ]+  }+staircase13 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 6000)]+  , pfence   = FFloor+  , ptopLeft = [ "·#·#·#·"+               , "#·#·#·#"+               , "·······"+               , "0·<S>·0"+               , "·······"+               , "#·#·#·#"+               , "·#·#·#·"+               ]+  }+staircase14 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 10000)]+  , pfence   = FGround+  , ptopLeft = [ "#·#·#·#"+               , "·#·#·#·"+               , "#·····#"+               , "··<S>··"+               , "#·····#"+               , "·#·#·#·"+               , "#·#·#·#"+               ]+  }+staircase15 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 20000)]+  , pfence   = FFloor+  , ptopLeft = [ "·#·#·#·#·"+               , "#·#·#·#·#"+               , "·#~~~~~#·"+               , "#~~<S>~~#"+               , "·#~~~~~#·"+               , "#·#·#·#·#"+               , "·#·#·#·#·"+               ]+  }+staircase16 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 20000)]+  , pfence   = FGround+  , ptopLeft = [ "#·#·#·#·#"+               , "·#·#·#·#·"+               , "#·······#"+               , "·#·<S>·#·"+               , "#·······#"+               , "·#·#·#·#·"+               , "#·#·#·#·#"+               ]+  }+staircase17 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 20000)]+  , pfence   = FFloor+  , ptopLeft = [ "#·#·#·#·#·#"+               , "·#·#·#·#·#·"+               , "#·#·····#·#"+               , "·#··<S>··#·"+               , "#·#·····#·#"+               , "·#·#·#·#·#·"+               , "#·#·#·#·#·#"+               ]+  }+staircase18 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 80000)]+  , pfence   = FGround+  , ptopLeft = [ "XX#·#·#·#XX"+               , "X#·#·#·#·#X"+               , "#·#·····#·#"+               , "·#··<S>··#·"+               , "#·#·····#·#"+               , "X#·#·#·#·#X"+               , "XX#·#·#·#XX"+               ]+  }+staircase19 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 20000)]+  , pfence   = FFloor+  , ptopLeft = [ "·#·#·#·#·#·"+               , "#·#·#·#·#·#"+               , "·#·······#·"+               , "#·#·<S>·#·#"+               , "·#·······#·"+               , "#·#·#·#·#·#"+               , "·#·#·#·#·#·"+               ]+  }+staircase20 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 5000)]+  , pfence   = FFloor+  , ptopLeft = [ "·#·#·0·#·#·"+               , "#·#·····#·#"+               , "·#··<S>··#·"+               , "#·#·····#·#"+               , "·#·#·I·#·#·"+               ]+  }+staircase21 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 5000)]+  , pfence   = FGround+  , ptopLeft = [ "#·#·I·#·#"+               , "·#·····#·"+               , "#··<S>··#"+               , "·#·····#·"+               , "#·#·0·#·#"+               ]+  }+staircase22 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 2000)]+  , pfence   = FGround+  , ptopLeft = [ "#·#·····#·#"+               , "·#··<S>··#·"+               , "#·#·····#·#"+               ]+  }+staircase23 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 1000)]+  , pfence   = FFloor+  , ptopLeft = [ "·#·······#·"+               , "#·#·<S>·#·#"+               , "·#·······#·"+               ]+  }+staircase24 = staircase+  { pfreq    = [(CLOSED_STAIRCASE, 1000)]+  , pfence   = FFloor+  , ptopLeft = [ "·#·····#·"+               , "#··<S>··#"+               , "·#·····#·"+               ]+  }+staircase25 = staircase+  { pfreq    = [(WALLED_STAIRCASE, 100)]+  , pfence   = FWall+  , ptopLeft = [ "·····"+               , "·<S>·"+               , "·····"+               ]+  }+staircase26 = staircase+  { pfreq    = [(WALLED_STAIRCASE, 200)]+  , pfence   = FWall+  , ptopLeft = [ "·······"+               , "··<S>··"+               , "·······"+               ]+  }+staircase27 = staircaseLift+  { pfreq    = [(WALLED_LIFT, 500)]+  , pfence   = FWall+  , ptopLeft = [ "#·····#"+               , "··<S>··"+               , "#·····#"+               ]+  }+staircase28 = staircaseLift+  { pfreq    = [(WALLED_LIFT, 1000)]+  , pfence   = FWall+  , ptopLeft = [ "·····"+               , "·····"+               , "·<S>·"+               , "·····"+               , "·····"+               ]+  }+staircase29 = staircase+  { pfreq    = [(WALLED_STAIRCASE, 1000)]+  , pfence   = FWall+  , ptopLeft = [ "#···#"+               , "·····"+               , "·<S>·"+               , "·····"+               , "#···#"+               ]+  }+staircase30 = staircaseLift+  { pfreq    = [(WALLED_LIFT, 1000)]+  , pfence   = FWall+  , ptopLeft = [ "#···#"+               , "·····"+               , "·<S>·"+               , "·····"+               , "#···#"+               ]+  }+staircase31 = staircase+  { pfreq    = [(WALLED_STAIRCASE, 2000)]+  , pfence   = FWall+  , ptopLeft = [ "·······"+               , "·~~~~~·"+               , "·~<S>~·"+               , "·~~~~~·"+               , "·······"+               ]+  }+staircase32 = staircaseLift+  { pfreq    = [(WALLED_LIFT, 5000)]+  , pfence   = FWall+  , ptopLeft = [ "#·····#"+               , "·······"+               , "··<S>··"+               , "·······"+               , "#·····#"+               ]+  }+staircase33 = staircase+  { pfreq    = [(WALLED_STAIRCASE, 5000)]+  , pfence   = FWall+  , ptopLeft = [ "#·#·#·#"+               , "·······"+               , "#·<S>·#"+               , "·······"+               , "#·#·#·#"+               ]+  }+staircase34 = staircaseLift+  { pfreq    = [(WALLED_LIFT, 5000)]+  , pfence   = FWall+  , ptopLeft = [ "·#·#·#·"+               , "#·····#"+               , "··<S>··"+               , "#·····#"+               , "·#·#·#·"+               ]+  }+staircase35 = staircase+  { pfreq    = [(WALLED_STAIRCASE, 1000)]+  , pfence   = FWall+  , ptopLeft = [ "·········"+               , "···<S>···"+               , "·········"+               ]+  }+staircase36 = staircaseLift+  { pfreq    = [(WALLED_LIFT, 1000)]+  , pfence   = FWall+  , ptopLeft = [ "·#·····#·"+               , "#··<S>··#"+               , "·#·····#·"+               ]+  }+staircase37 = staircase+  { pfreq    = [(WALLED_STAIRCASE, 1000)]+  , pfence   = FWall+  , ptopLeft = [ "·········"+               , "·0·<S>·0·"+               , "·········"+               ]+  }++-- * Allure-specific++overrideLift :: [(Char, GroupName TileKind)]+overrideLift = [ ('<', STAIRCASE_LIFT_UP), ('>', STAIRCASE_LIFT_DOWN)+               , ('I', SIGNBOARD), ('S', S_LIFT_SHAFT) ]+staircaseLift11 = staircase11+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 2000)]  -- weak cover, low freq+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift12 = staircase12+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 4000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift13 = staircase13+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 6000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift14 = staircase14+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 10000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift15 = staircase15+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 20000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift16 = staircase16+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 20000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift17 = staircase17+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 20000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift18 = staircase18+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 80000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift19 = staircase19+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 20000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift20 = staircase20+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 5000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift21 = staircase21+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 5000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift22 = staircase22+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 2000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift23 = staircase23+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 1000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift24 = staircase24+  { pname     = "a lift"+  , pfreq     = [(CLOSED_LIFT, 1000)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+staircaseLift25 = staircase25+  { pname     = "a lift"+  , pfreq     = [(WALLED_LIFT, 100)]+  , poverrideDark = overrideLift+  , poverrideLit = overrideLift+  }+pumps = PlaceKind+  { psymbol  = 'w'+  , pname    = "water pumps"+  , pfreq    = [ (ROGUE, 200), (LABORATORY, 100), (EMPTY, 2000)+               , (SHOOTOUT, 50), (RAID, 300) ]+  , prarity  = [(1, 1)]+  , pcover   = CAlternate+  , pfence   = FWall+  , ptopLeft = [ "·f"+               , "d;"+               ]+  , poverrideDark = [ ('·', DAMP_FLOOR_DARK)+                    , ('d', DOORLESS_MACHINERY)+                    , ('f', PUMPS_DARK)+                    , (';', UNDERBRUSH_CLUMP_DARK) ]+  , poverrideLit = [ ('·', DAMP_FLOOR_LIT)+                   , ('d', DOORLESS_MACHINERY)+                   , ('f', PUMPS_LIT)+                   , (';', UNDERBRUSH_CLUMP_LIT) ]+  }+oval = PlaceKind+  { psymbol  = 'o'+  , pname    = "a dome"+  , pfreq    = [ (ROGUE, 20000), (ARENA, 30000), (MUSEUM, 30000)+               , (LABORATORY, 50000), (EMPTY, 3000), (EXIT, 5000)+               , (AMBUSH, 20000), (RAID, 20000) ]+  , prarity  = [(1, 1)]+  , pcover   = CStretch+  , pfence   = FWall+  , ptopLeft = [ "####·"+               , "##···"+               , "#··tt"+               , "#·t··"+               , "··t··"+               ]+  , poverrideDark = [ ('t', TRAIL_LIT)+                    , ('p', TRAPPED_DOOR)+                    , ('b', BARREL)+                    , ('a', SAFE_TRAIL_LIT)+                    , ('1', STUCK_DOOR)+                    , ('2', TRAPPED_DOOR)+                    , ('T', S_TREE_LIT)+                    , ('~', S_POOL_DARK)+                    , (';', S_UNDERBRUSH_DARK) ]+  , poverrideLit = [ ('t', TRAIL_LIT)+                   , ('p', TRAPPED_DOOR)+                   , ('b', BARREL)+                   , ('a', SAFE_TRAIL_LIT)+                   , ('2', STUCK_DOOR)  -- reversed vs dark+                   , ('1', TRAPPED_DOOR)+                   , ('T', S_TREE_LIT)+                   , ('~', S_POOL_LIT)+                   , (';', S_UNDERBRUSH_LIT) ]+  }+ovalFloor = oval+  { pfreq    = [ (ROGUE, 150000), (ARENA, 60000), (MUSEUM, 60000)+               , (LABORATORY, 100000), (EMPTY, 20000), (EXIT, 5000)+               , (AMBUSH, 100000), (RAID, 150000) ]+  , pfence   = FGround+  , ptopLeft = [ "aXXX##"+               , "Xp###·"+               , "X#a···"+               , "X#·a·a"+               , "##··a;"+               , "#··a;;"+               ]+  }+ovalSquare = oval+  { pfreq    = [ (ROGUE, 20000), (ARENA, 30000), (MUSEUM, 30000)+               , (LABORATORY, 50000), (EMPTY, 8000), (EXIT, 5000)+               , (AMBUSH, 20000), (RAID, 20000) ]+  , pfence   = FGround+  , ptopLeft = [ "X###+"+               , "##···"+               , "#··;;"+               , "#·;;;"+               , "+·;;;"+               ]+  }+ovalBasin = oval+  { pname    = "a water basin"+  , pfreq    = [ (ROGUE, 100000), (ARENA, 100000), (LABORATORY, 200000)+               , (EMPTY, 15000), (RAID, 100000) ]+  , pfence   = FGround+  , ptopLeft = [ "XXX1##"+               , "X###··"+               , "X#····"+               , "2#··~~"+               , "#··~~~"+               , "#··~~~"+               ]+  }+ovalBasin2 = oval+  { pname    = "a water basin"+  , pfreq    = [ (ROGUE, 600), (ARENA, 10000), (LABORATORY, 3000)+               , (EMPTY, 700), (RAID, 600) ]+  , pfence   = FWall+  , ptopLeft = [ "#···"+               , "··~~"+               , "·~~~"+               , "·~~~"+               ]+  }+squareBasin = oval+  { pname    = "a water basin"+  , pfreq    = [(ARENA, 10000), (LABORATORY, 5000), (EMPTY, 2000), (RAID, 1000)]+      -- keep it less common in raid not to overload the newbies+  , pfence   = FNone+  , ptopLeft = [ "0bt0t"+               , "b~~~~"+               , "t~0~~"+               , "0~~~~"+               , "t~~~~"+               ]+  }+squareBasin2 = oval+  { pname    = "a water basin"+  , pfreq    = [(ARENA, 200000), (EMPTY, 20000), (BRAWL, 100000)]+      -- can't do LABORATORY, because barrels might block corridors+  , pfence   = FNone+  , ptopLeft = [ "T;T;;;"+               , ";~~~~~"+               , "T~~~~~"+               , ";~~0~~"+               , ";~~~~~"+               , "b~~~~~"+               ]+  }+floodedRoom = PlaceKind  -- Valid for any nonempty area, hence low frequency.+  { psymbol  = 'f'+  , pname    = "a flooded room"+  , pfreq    = [(ROGUE, 10), (LABORATORY, 12), (ZOO, 50), (RAID, 7)]+  , prarity  = [(1, 1)]+  , pcover   = CStretch+  , pfence   = FWall+  , ptopLeft = ["~"]+  , poverrideDark = []+  , poverrideLit = []+  }+floodedRoom2 = PlaceKind+  { psymbol  = 'p'+  , pname    = "a pond"+  , pfreq    = [(BRAWL, 100)]+  , prarity  = [(1, 1)]+  , pcover   = CMirror+  , pfence   = FNone+  , ptopLeft = [ "XXf"+               , "f~~"+               , "~~X" ]+  , poverrideDark = [('f', PUMPS_LIT)]+  , poverrideLit = [('f', PUMPS_LIT)]+  }+maze = PlaceKind+  { psymbol  = 'm'+  , pname    = "an intricate maze"+  , pfreq    = [ (ROGUE, 60), (LABORATORY, 1500), (ARENA, 3)+               , (MUSEUM, 3), (EXIT, 100), (RAID, 60) ]+  , prarity  = [(1, 1)]+  , pcover   = CStretch+  , pfence   = FWall+  , ptopLeft = [ "##··"+               , "#··#"+               , "··#·"+               ]+  , poverrideDark = [ ('·', OILY_FLOOR_DARK)+                    , ('&', CACHE_MAZE)+                    , ('p', TRAPPED_DOOR)+                    , ('i', FLOOR_ACTOR_ITEM)  -- lit or not, randomly+                    , ('f', BUSH_GROVE_DARK)+                    , (';', S_UNDERBRUSH_DARK)+                    , ('$', TRAPPABLE_WALL) ]+  , poverrideLit = [ ('·', OILY_FLOOR_LIT)+                   , ('&', CACHE_MAZE)+                   , ('p', TRAPPED_DOOR)+                   , ('i', FLOOR_ACTOR_ITEM)  -- lit or not, randomly+                   , ('f', BUSH_GROVE_LIT)+                   , (';', S_UNDERBRUSH_LIT)+                   , ('$', TRAPPABLE_WALL) ]+  }+maze2 = maze+  { pfreq    = [ (ROGUE, 120), (LABORATORY, 12000), (ARENA, 4)+               , (MUSEUM, 4), (EXIT, 100), (RAID, 120) ]+  , ptopLeft = [ "#·%%·"+               , "·%··#"+               , "···#·"+               ]+  }+maze3 = maze+  { pfreq    = [ (ROGUE, 120), (LABORATORY, 1000), (ARENA, 8)+               , (MUSEUM, 4), (EMPTY, 300), (EXIT, 50), (RAID, 120) ]+  , ptopLeft = [ "#·ff·"+               , "·f··#"+               , "···#;"+               ]+  }+maze4 = maze+  { pfreq    = [ (ROGUE, 300), (LABORATORY, 15000), (ARENA, 9)+               , (EXIT, 200), (RAID, 300) ]+  , ptopLeft = [ "##·##·"+               , "#·#··#"+               , "~·f···"+               ]+  }+mazeBig = maze+  { pfreq    = [ (ROGUE, 1500), (LABORATORY, 8000), (ARENA, 10000)+               , (EXIT, 1000), (RAID, 300) ]+  , pfence   = FNone+  , ptopLeft = [ "X$$$$$"+               , "$··##·"+               , "$#····"+               , "$#·p%%"+               , "$··%:i"+               ]+  }+mazeBig2 = mazeBig+  { ptopLeft = [ "XX$$$~"+               , "X#···%"+               , "$·###·"+               , "$·p&%%"+               , "$·#iii"+               ]+  }+cells = PlaceKind+  { psymbol  = '#'+  , pname    = "air filters"+  , pfreq    = [ (ROGUE, 40), (LABORATORY, 48), (MUSEUM, 10)+               , (EXIT, 150), (NOISE, 480)+               , (ZOO, 700), (AMBUSH, 80), (RAID, 40) ]+  , prarity  = [(1, 1)]+  , pcover   = CReflect+  , pfence   = FWall+  , ptopLeft = [ "#··"+               , "·d·"+               , "··#"+               ]+  , poverrideDark = [ ('d', DOORLESS_MACHINERY), ('b', RUBBLE_OR_WASTE_DARK)+                    , ('f', BUSH_GROVE_DARK), ('o', OIL_RESIDUE_DARK)+                    , (';', UNDERBRUSH_CLUMP_DARK), ('w', S_REINFORCED_WALL) ]+  , poverrideLit = [ ('d', DOORLESS_MACHINERY), ('b', RUBBLE_OR_WASTE_LIT)+                   , ('f', BUSH_GROVE_LIT), ('o', OIL_RESIDUE_LIT)+                   , (';', UNDERBRUSH_CLUMP_LIT), ('w', S_REINFORCED_WALL) ]+  }+cells2 = cells+  { pname    = "humidity equalizers"+  , prarity  = [(1, 2), (10, 2)]+  , ptopLeft = [ "f;#·"  -- extra column to avoid blocked exits+               , ";d;;"+               , "·db;"+               ]+  }+cells3 = cells+  { pname    = "thermostat units"+  , ptopLeft = [ "·^#"+               , "·#~"+               , ";;#"+               ]+  }+cells4 = cells+  { pname    = "a power node"+  , ptopLeft = [ "·o#"+               , "oob"+               , "#b·"+               ]+  }+cells5 = cells  -- this one is distinct enough from others, so needs a boost+  { pname    = "broken robot holds"+  , pfreq    = [ (ROGUE, 20), (LABORATORY, 15)+               , (EMPTY, 80), (EXIT, 70), (NOISE, 150) ]+  , ptopLeft = [ "··w"+               , "·:w"+               , "wwo"+               ]+  }+cells6 = cells+  { pname    = "animal holding pens"+  , pfreq    = [ (ARENA, 3), (LABORATORY, 20), (ZOO, 80)]+  , ptopLeft = [ ";;f"+               , "%%'"+               ]+  }+cells7 = cells+  { pname    = "a defunct control room"+  , pfreq    = [ (ROGUE, 10), (LABORATORY, 20)+               , (EXIT, 30), (NOISE, 200), (AMBUSH, 50) ]+  , pfence   = FFloor+  , ptopLeft = [ "d·o"+               , "·#o"+               ]+  }+tank = PlaceKind+  { psymbol  = 'c'+  , pname    = "a tank"+  , pfreq    = [(EMPTY, 1)]+      -- no point taking up space if very little space taken,+      -- but if no other place can be generated, a failsafe is useful+  , prarity  = [(1, 1)]+  , pcover   = CStretch+  , pfence   = FNone+  , ptopLeft = [ "#"+               ]+  , poverrideDark = [ ('#', DOORLESS_WALL)+                    , ('r', S_REINFORCED_WALL)+                    , ('b', BARREL) ]+  , poverrideLit = [ ('#', DOORLESS_WALL)+                   , ('r', S_REINFORCED_WALL)+                   , ('b', BARREL) ]+  }+tank2 = tank+  { pname    = "a barrel stack"+  , pfreq    = [(EMPTY, 30), (EXIT, 2), (NOISE, 1), (BATTLE, 1)]+  , ptopLeft = [ "b"+               ]+  }+tank3 = tank+  { pfreq    = [(EMPTY, 150), (EXIT, 15), (NOISE, 50), (BATTLE, 25)]+  , ptopLeft = [ "0#"+               , "##"+               ]+  }+tank4 = tank+  { pname    = "a barrel stack"+  , pfreq    = [(EMPTY, 150), (EXIT, 8), (NOISE, 50), (BATTLE, 25)]+  , ptopLeft = [ "Xb"+               , "bb"+               ]+  }+tank5 = tank+  { pname    = "a barrel yard"+  , pfreq    = [(EMPTY, 1800), (EXIT, 700), (NOISE, 700), (BATTLE, 300)]+  , pcover   = CAlternate+  , ptopLeft = [ "bbX"+               , "bbX"+               , "XXX"+               ]+  }+tank6 = tank+  { pname    = "a barrel yard"+  , pfreq    = [(EMPTY, 15000), (EXIT, 5000), (NOISE, 5000), (BATTLE, 2500)]+  , pcover   = CAlternate+  , ptopLeft = [ "bbbX"+               , "bbbX"+               , "bbbX"+               , "XXXX"+               ]+  }+tank7 = tank+  { pfreq    = [(EMPTY, 300), (EXIT, 5), (NOISE, 100), (BATTLE, 50)]+  , ptopLeft = [ "rr#"+               , "r##"+               , "###"+               ]+  }+tank8 = tank+  { pfreq    = [(EMPTY, 500), (EXIT, 15), (NOISE, 150), (BATTLE, 70)]+  , ptopLeft = [ "XX0#"+               , "Xrr#"+               , "0r##"+               , "####"+               ]+  }+tank9 = tank+  { pname    = "a barrel yard"+  , pfreq    = [(EMPTY, 500), (EXIT, 150), (NOISE, 150), (BATTLE, 70)]+  , pcover   = CReflect+  , ptopLeft = [ "XbbX"+               , "bbbX"+               , "bbbX"+               , "XXXX"+               ]+  }+tank10 = tank+  { pname    = "a cistern"+  , pfreq    = [(EMPTY, 500), (EXIT, 15), (NOISE, 150), (BATTLE, 70)]+  , ptopLeft = [ "XXr#"+               , "Xr##"+               , "r###"+               , "####"+               ]+  }+tank11 = tank+  { pname    = "a barrel yard"+  , pfreq    = [(EMPTY, 700), (EXIT, 250), (NOISE, 250), (BATTLE, 125)]+  , pcover   = CReflect+  , ptopLeft = [ "bbbXX"+               , "bbbbX"+               , "XbbbX"+               , "XXXXX"+               ]+  }+tank12 = tank+  { pname    = "a barrel yard"+  , pfreq    = [(EMPTY, 1000), (EXIT, 500), (NOISE, 500), (BATTLE, 250)]+  , pcover   = CReflect+  , ptopLeft = [ "XbbXX"+               , "bbbbX"+               , "bbbbX"+               , "Xbbbb"+               , "XXXbb"+               ]+  }+shuttleHusk = PlaceKind+  { psymbol  = 's'+  , pname    = "a shuttle husk"+  , pfreq    = [(EMPTY, 1000), (EXIT, 15000), (AMBUSH, 15000)]+  , prarity  = [(1, 1)]+  , pcover   = CMirror+  , pfence   = FGround+  , ptopLeft = [ "X·###·X"  -- 7 x 9+               , "X%#w#%X"+               , "#%···%#"+               , "#··h··#"+               , "#w··rw#"+               , "···rr&c"+               , "###&###"+               , "XhhchhX"+               , "hh#w#hh"+               ]+  , poverrideDark = [ ('·', OILY_FLOOR_DARK)+                    , ('r', RUBBLE_OR_WASTE_DARK)+                    , ('#', S_SHUTTLE_HULL)+                    , ('c', CACHE_SHUTTLE)+                    , ('u', STUCK_DOOR)+                    , ('h', S_HARDWARE_RACK)+                    , ('w', S_REINFORCED_WALL) ]+  , poverrideLit = [ ('·', OILY_FLOOR_LIT)+                   , ('r', RUBBLE_OR_WASTE_LIT)+                   , ('#', S_SHUTTLE_HULL)+                   , ('c', CACHE_SHUTTLE)+                   , ('u', STUCK_DOOR)+                   , ('h', S_HARDWARE_RACK)+                   , ('w', S_REINFORCED_WALL) ]+  }+shuttleHusk2 = shuttleHusk+  { pfreq    = [(EMPTY, 1000), (EXIT, 15000), (AMBUSH, 15000)]+  , ptopLeft = map (T.cons 'X' . flip T.snoc 'X')+               $ ptopLeft shuttleHusk  -- 9 x 9+  }+shuttleHusk3 = shuttleHusk+  { pfreq    = [(EMPTY, 300), (EXIT, 5000), (AMBUSH, 5000)]+  , ptopLeft = [ ":··##··X"  -- 8 x 8+               , "X#%ww%#X"+               , "#w····w#"+               , "····h·r#"+               , "#·rrr&r#"+               , "###&&###"+               , "XhhcchhX"+               , "hh#ww#hh"+               ]+  }+shuttleHusk4 = shuttleHusk3+  { pfreq    = [(EMPTY, 300), (EXIT, 5000), (AMBUSH, 5000)]+  , ptopLeft = map (T.cons 'X' . flip T.snoc 'X')+               $ ptopLeft shuttleHusk3  -- 10 x 8+  }+shuttleHusk5 = shuttleHusk+  { pfreq    = [(EXIT, 80000), (AMBUSH, 80000)]+      -- can't have EMPTY or AI can't reach and kill the boss and get the key+  , ptopLeft = [ "···##···"  -- 8 x 10+               , "w#%ww%#w"+               , "X#····#X"+               , "Xu··h·#X"+               , "#w····w#"+               , "%rr····%"+               , "##&rrr##"+               , "X##&&##X"+               , "Xhhcc&hX"+               , "hh#w&#hh"+               ]+  }+shuttleHusk6 = shuttleHusk+  { pfreq    = [(EMPTY, 2000), (EXIT, 120000), (AMBUSH, 120000)]+  , ptopLeft = [ "X··###··X"  -- 9 x 10+               , "X#%#w#%#X"+               , "##·h·h·##"+               , "········%"+               , "#w·····w#"+               , "%·····rr%"+               , "##·rr&r##"+               , "X###&###X"+               , ":XhhchhXX"+               , "Xhh#w#hhX"+               ]+  }+dormitory = PlaceKind+  { psymbol  = 'd'+  , pname    = "dormitory"+  , pfreq    = [(RESIDENTIAL, 10000)]+  , prarity  = [(1, 1)]+  , pcover   = CAlternate+  , pfence   = FWall+  , ptopLeft = [ "··#"+               , "··#"+               , "+##"+               , "ddd"+               ]+  , poverrideDark = [ ('d', FLOOR_ACTOR_ITEM_LIT)+                    , ('f', PUMPS_LIT)+                    , ('$', TRAPPABLE_WALL) ]+  , poverrideLit = [ ('d', FLOOR_ACTOR_ITEM_LIT)+                   , ('f', PUMPS_LIT)+                   , ('$', TRAPPABLE_WALL) ]+  }+dormitory2 = dormitory+  { pfreq    = [(RESIDENTIAL, 10000)]+  , ptopLeft = [ "··+d"+               , "··#d"+               , "###d"+               ]+  }+dormitory3 = dormitory+  { pfreq    = [(RESIDENTIAL, 2000)]+  , pcover   = CStretch+  , ptopLeft = [ "··#··"+               , "··+··"+               , "#+###"+               , "ddddd"+               ]+  }+dormitory4 = dormitory2+  { pfreq    = [(RESIDENTIAL, 10000)]+  , pcover   = CStretch+  , ptopLeft = [ "···#d"  -- avoid huge corridor and tiny room+               , "···+d"+               , "##+#d"+               , "···#d"+               , "···#d"+               ]+  }+dormitory5 = dormitory+  { pfreq    = [(RESIDENTIAL, 100)]+  , pcover   = CMirror+  , pfence   = FNone+  , ptopLeft = [ "##$$$$$$$$$##"+               , "f#··#···+··#f"+               , "d#··+···#··+d"+               , "d#####+#####d"+               , "ddddddddddddd"+               ]+  }+dormitory6 = dormitory+  { pfreq    = [(RESIDENTIAL, 100)]+  , pcover   = CMirror+  , pfence   = FNone+  , ptopLeft = [ "#fddd"+               , "##+#d"+               , "$··#d"+               , "$··#d"+               , "$+##d"+               , "$··#d"+               , "$··#d"+               , "$··#d"+               , "##+#d"+               , "#fddd"+               ]+  }++-- * Helper functions++switchExitToUp :: Text -> PlaceKind -> PlaceKind+switchExitToUp terminal s = s+ { psymbol   = '<'+ , pname     = pname s <+> "up"+ , pfreq     = map (\(t, k) -> (GroupName $ fromGroupName t <+> "up", k))+               $ pfreq s+ , poverrideDark = ('>', GroupName $ terminal <+> "Dark")+                   : filter ((/= '>') . fst) (poverrideDark s)+ , poverrideLit = ('>', GroupName $ terminal <+> "Lit")+                  : filter ((/= '>') . fst) (poverrideLit s)+ }++switchExitToDown :: Text -> PlaceKind -> PlaceKind+switchExitToDown terminal s = s+ { psymbol   = '>'+ , pname     = pname s <+> "down"+ , pfreq     = map (\(t, k) -> (GroupName $ fromGroupName t <+> "down", k))+               $ pfreq s+ , poverrideDark = ('<', GroupName $ terminal <+> "Dark")+                   : filter ((/= '<') . fst) (poverrideDark s)+ , poverrideLit = ('<', GroupName $ terminal <+> "Lit")+                  : filter ((/= '<') . fst) (poverrideLit s)+ }+++overrideGatedStaircase :: [(Char, GroupName TileKind)]+overrideGatedStaircase =+  [ ('<', GATED_STAIRCASE_UP), ('>', GATED_STAIRCASE_DOWN)+  , ('I', SIGNBOARD), ('S', FILLER_WALL) ]++switchStaircaseToGated :: PlaceKind -> PlaceKind+switchStaircaseToGated s = s+ { psymbol   = 'g'+ , pname     = T.unwords $ "a gated" : tail (T.words (pname s))+ , pfreq     = map (first (\t -> GroupName $ "gated" <+> fromGroupName t))+               $ pfreq s+ , poverrideDark = overrideGatedStaircase+ , poverrideLit = overrideGatedStaircase+ }++overrideGatedLift :: [(Char, GroupName TileKind)]+overrideGatedLift =+  [ ('<', GATED_LIFT_UP), ('>', GATED_LIFT_DOWN)+  , ('I', SIGNBOARD), ('S', S_LIFT_SHAFT) ]++switchLiftToGated :: PlaceKind -> PlaceKind+switchLiftToGated s = s+ { psymbol   = 'g'+ , pname     = T.unwords $ "a gated" : tail (T.words (pname s))+ , pfreq     = map (first (\t -> GroupName $ "gated" <+> fromGroupName t))+               $ pfreq s+ , poverrideDark = overrideGatedLift+ , poverrideLit = overrideGatedLift+ }+++overrideDeconStaircase :: [(Char, GroupName TileKind)]+overrideDeconStaircase =+  [ ('<', DECON_STAIRCASE_UP)+  , ('>', S_STAIRCASE_TRAP_DOWN_OIL)  -- talter high enough+  , ('I', SIGNBOARD), ('S', FILLER_WALL) ]++switchStaircaseToDecon :: PlaceKind -> PlaceKind+switchStaircaseToDecon s = s+ { psymbol   = 'd'+ , pfreq     = map (first (\t -> GroupName $ "decon" <+> fromGroupName t))+                   (pfreq s)+ , poverrideDark = overrideDeconStaircase+ , poverrideLit = overrideDeconStaircase+ }++overrideDeconLift :: [(Char, GroupName TileKind)]+overrideDeconLift =+  [ ('<', DECON_LIFT_UP)+  , ('>', STAIRCASE_LIFT_DOWN)+  , ('I', SIGNBOARD), ('S', S_LIFT_SHAFT) ]++switchLiftToDecon :: PlaceKind -> PlaceKind+switchLiftToDecon s = s+ { psymbol   = 'd'+ , pfreq     = map (first (\t -> GroupName $ "decon" <+> fromGroupName t))+                   (pfreq s)+ , poverrideDark = overrideDeconLift+ , poverrideLit = overrideDeconLift+ }+++overrideWeldedStaircase :: [(Char, GroupName TileKind)]+overrideWeldedStaircase =+  [ ('<', WELDED_STAIRCASE_UP), ('>', ORDINARY_STAIRCASE_DOWN)+  , ('I', SIGNBOARD), ('S', FILLER_WALL) ]++switchStaircaseToWelded :: PlaceKind -> PlaceKind+switchStaircaseToWelded s = s+ { psymbol   = 'w'+ , pfreq     = map (first (\t -> GroupName $ "welded" <+> fromGroupName t))+               $ pfreq s+ , poverrideDark = overrideWeldedStaircase+ , poverrideLit = overrideWeldedStaircase+ }++overrideWeldedLift :: [(Char, GroupName TileKind)]+overrideWeldedLift =+  [ ('<', WELDED_LIFT_UP), ('>', ORDINARY_LIFT_DOWN)+  , ('I', SIGNBOARD), ('S', S_LIFT_SHAFT) ]++switchLiftToWelded :: PlaceKind -> PlaceKind+switchLiftToWelded s = s+ { psymbol   = 'w'+ , pfreq     = map (first (\t -> GroupName $ "welded" <+> fromGroupName t))+               $ pfreq s+ , poverrideDark = overrideWeldedLift+ , poverrideLit = overrideWeldedLift+ }+++overrideOutdoor :: [(Char, GroupName TileKind)]+overrideOutdoor =+  [ ('<', STAIRCASE_OUTDOOR_UP), ('>', STAIRCASE_OUTDOOR_DOWN)+  , ('I', SIGNBOARD), ('S', FILLER_WALL) ]++switchStaircaseToOutdoor :: PlaceKind -> PlaceKind+switchStaircaseToOutdoor s = s+ { psymbol   = 'o'+ , pname     = "an outdoor area exit"+ , pfreq     = map (first (\t -> GroupName $ "outdoor" <+> fromGroupName t))+               $ pfreq s+ , poverrideDark = overrideOutdoor+ , poverrideLit = overrideOutdoor+ }++switchEscapeToUp :: PlaceKind -> PlaceKind+switchEscapeToUp s = s+ { psymbol   = '<'+ , pname     = "an escape up"+ , pfreq     = map (\(_, n) -> (INDOOR_ESCAPE_UP, n)) $ pfreq s+ , poverrideDark = ('>', ESCAPE_UP) : poverrideDark s+ , poverrideLit = ('>', ESCAPE_UP) : poverrideLit s+ }++switchEscapeToOutdoorDown :: PlaceKind -> PlaceKind+switchEscapeToOutdoorDown s = s+ { pname     = "outdoor escape route"+ , pfreq     = map (\(_, n) -> (OUTDOOR_ESCAPE_DOWN, n)) $ pfreq s+ , poverrideDark = ('>', ESCAPE_OUTDOOR_DOWN) : poverrideDark s+ , poverrideLit = ('>', ESCAPE_OUTDOOR_DOWN) : poverrideLit s+ }++switchEscapeToSpaceshipDown :: PlaceKind -> PlaceKind+switchEscapeToSpaceshipDown s = s+ { pname     = "escape from spaceship"+ , pfreq     = map (\(_, n) -> (ESCAPE_FROM_SPACESHIP_DOWN, n)) $ pfreq s+ , poverrideDark = ('>', ESCAPE_SPACESHIP_DOWN) : poverrideDark s+ , poverrideLit = ('>', ESCAPE_SPACESHIP_DOWN) : poverrideLit s  }
GameDefinition/Content/RuleKind.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE TemplateHaskell #-} -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE.@@ -12,12 +12,17 @@  import Prelude () -import Language.Haskell.TH.Syntax-import System.FilePath-import System.IO (readFile)+import Game.LambdaHack.Core.Prelude +import qualified Data.Ini.Reader as Ini+import           Instances.TH.Lift ()+import           Language.Haskell.TH.Syntax+import           System.FilePath+import           System.IO+  (IOMode (ReadMode), hGetContents, hSetEncoding, openFile, utf8)+ -- Cabal-import qualified Paths_Allure as Self (getDataFileName, version)+import qualified Paths_Allure as Self (version)  import Game.LambdaHack.Content.RuleKind @@ -26,9 +31,6 @@   { rtitle = "Allure of the Stars"   , rXmax = 80   , rYmax = 42-  , rfontDir = $(do-      x <- qRunIO (Self.getDataFileName "GameDefinition/fonts")-      lift x)   , rexeVersion = Self.version   -- The strings containing the default configuration file   -- included from config.ui.default.@@ -36,12 +38,18 @@   , rcfgUIDefault = $(do       let path = "GameDefinition" </> "config.ui" <.> "default"       qAddDependentFile path-      x <- qRunIO (readFile path)-      lift x)+      s <- qRunIO $ do+        inputHandle <- openFile path ReadMode+        hSetEncoding inputHandle utf8+        hGetContents inputHandle+      let cfgUIDefault =+            either (error . ("Ini.parse of default config" `showFailure`)) id+            $ Ini.parse s+      lift (s, cfgUIDefault))   , rwriteSaveClips = 1000   , rleadLevelClips = 50   , rscoresFile = "Allure.scores"-  , rnearby = 20+  , rnearby = 30   , rstairWordCarried = ["staircase", "lift"]   , rsymbolProjectile = '{'   }
GameDefinition/Content/TileKind.hs view
@@ -1,878 +1,1455 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)--- This file is a part of the computer game Allure of the Stars--- and is released under the terms of the GNU Affero General Public License.--- For license and copyright information, see the file LICENSE.------ | Terrain tile definitions.-module Content.TileKind-  ( content-  ) where--import Prelude ()--import Game.LambdaHack.Core.Prelude--import qualified Data.Text as T--import Game.LambdaHack.Content.TileKind-import Game.LambdaHack.Definition.Color-import Game.LambdaHack.Definition.Defs--content :: [TileKind]-content =-  [unknown, unknownOuterFence, basicOuterFence, bedrock, wall, wallSuspect, wallObscured, wallObscuredDefaced, wallObscuredFrescoed, pillar, pillarCache, lampPost, signboardUnread, signboardRead, tree, treeBurnt, treeBurning, rubble, rubbleSpice, doorTrapped, doorClosed, stairsUp, stairsTrappedUp, stairsOutdoorUp, stairsGatedUp, stairsDown, stairsTrappedDown, stairsOutdoorDown, stairsGatedDown, escapeUp, escapeDown, escapeOutdoorDown, wallGlass, wallGlassSpice, pillarIce, pulpit, bush, bushBurnt, bushBurning, fog, fogDark, smoke, smokeDark, doorOpen, floorCorridor, floorArena, floorDamp, floorDirt, floorDirtSpice, floorActor, floorActorItem, floorAshes, shallowWater, shallowWaterSpice, shallowWater2, floorRed, floorBlue, floorGreen, floorBrown, floorArenaShade ]-  ++ map makeDarkColor ldarkColorable-  -- Allure-specific-  ++ [oriel, outerHullWall, rubbleBurning, rubbleBurningSpice, wallOpenable, wallObscuredSafety, wallObscured3dBillboard, liftShaft, rock, pillarCache2, pillarCache3, stairsDecontaminatingUp, stairsWelded, stairsLiftUp, stairsLiftTrappedUp, stairsLiftGatedUp, stairsLiftDecontaminatingUp, stairsLiftWelded, stairsDecontaminatingDown, stairsLiftDown, stairsLiftTrappedDown, stairsLiftGatedDown, stairsLiftDecontaminatingDown, escapeSpaceshipDown, emptyAirlock, reinforcedWall, reinforcedWallSpice, wallShuttle, wallShuttleSpice, machineWall, machineWallSpice, floorOily, oilSpill, oilSpillSpice, floorWindow]--unknown,    unknownOuterFence, basicOuterFence, bedrock, wall, wallSuspect, wallObscured, wallObscuredDefaced, wallObscuredFrescoed, pillar, pillarCache, lampPost, signboardUnread, signboardRead, tree, treeBurnt, treeBurning, rubble, rubbleSpice, doorTrapped, doorClosed, stairsUp, stairsTrappedUp, stairsOutdoorUp, stairsGatedUp, stairsDown, stairsTrappedDown, stairsOutdoorDown, stairsGatedDown, escapeUp, escapeDown, escapeOutdoorDown, wallGlass, wallGlassSpice, pillarIce, pulpit, bush, bushBurnt, bushBurning, fog, fogDark, smoke, smokeDark, doorOpen, floorCorridor, floorArena, floorDamp, floorDirt, floorDirtSpice, floorActor, floorActorItem, floorAshes, shallowWater, shallowWaterSpice, shallowWater2, floorRed, floorBlue, floorGreen, floorBrown, floorArenaShade :: TileKind--- Allure-specific-oriel,       outerHullWall, rubbleBurning, rubbleBurningSpice, wallOpenable, wallObscuredSafety, wallObscured3dBillboard, liftShaft, rock, pillarCache2, pillarCache3, stairsDecontaminatingUp, stairsWelded, stairsLiftUp, stairsLiftTrappedUp, stairsLiftGatedUp, stairsLiftDecontaminatingUp, stairsLiftWelded, stairsDecontaminatingDown, stairsLiftDown, stairsLiftTrappedDown, stairsLiftGatedDown, stairsLiftDecontaminatingDown, escapeSpaceshipDown, emptyAirlock, reinforcedWall, reinforcedWallSpice, wallShuttle, wallShuttleSpice, machineWall, machineWallSpice, floorOily, oilSpill, oilSpillSpice, floorWindow :: TileKind--ldarkColorable :: [TileKind]-ldarkColorable = [tree, bush, floorCorridor, floorArena, floorDamp, floorDirt, floorDirtSpice, floorActor, floorActorItem, shallowWater, shallowWaterSpice, shallowWater2, floorOily]---- Symbols to be used:---         LOS    noLOS--- Walk    .'~     :;--- noWalk  %^     #O&<>+------ can be opened ^&+--- can be closed '--- some noWalk can be changed without opening, regardless of symbol--- not used yet:--- : (curtain, etc., not flowing, but solid and static)--- ` (not distinct enough from ' and already used for some blasts)---- Note that for AI hints and UI comfort, most multiple-use @Embed@ tiles--- should have a variant, which after first use transforms into a different--- colour tile without @ChangeTo@ and similar (which then AI no longer touches).--- If a tile is supposed to be repeatedly activated by AI (e.g., cache),--- it should keep @ChangeTo@ for the whole time.---- * Main tiles, modified for Allure; some removed---- ** Not walkable---- *** Not clear--unknown = TileKind  -- needs to have index 0 and alter 1; no other with 1-  { tsymbol  = ' '-  , tname    = "unknown space"-  , tfreq    = [("unknown space", 1)]-  , tcolor   = defFG-  , tcolor2  = defFG-  , talter   = 1-  , tfeature = [Dark]-  }-unknownOuterFence = TileKind-  { tsymbol  = ' '-  , tname    = "unknown space"-  , tfreq    = [("unknown outer fence", 1)]-  , tcolor   = defFG-  , tcolor2  = defFG-  , talter   = maxBound  -- impenetrable-  , tfeature = [Dark]-  }-basicOuterFence = TileKind-  { tsymbol  = '#'-  , tname    = "habitat containment wall"-  , tfreq    = [("habitat containment wall", 1)]-  , tcolor   = BrBlack-  , tcolor2  = BrBlack-  , talter   = maxBound  -- impenetrable-  , tfeature = []-  }-bedrock = TileKind-  { tsymbol  = '#'-  , tname    = "wall"-  , tfreq    = [ ("fillerWall", 1), ("legendLit", 100), ("legendDark", 100)-               , ("rogueSet", 60), ("museumSetDark", 4), ("noiseSetLit", 450)-               , ("powerSetDark", 450), ("battleSetDark", 250)-               , ("escapeSetDark", 4)-               , ("stair terminal Lit", 100), ("stair terminal Dark", 100)-               , ("doorlessWallOver_#", 80), ("doorlessMachineryOver_#", 1) ]-  , tcolor   = BrWhite-  , tcolor2  = defFG-  , talter   = 100-  , tfeature = []-  }-wall = bedrock-  { tfreq    = [("trappableWall", 1), ("rectWindowsOver_%", 80)]-  , tfeature = [BuildAs "suspect wall"]-  }-wallSuspect = TileKind  -- only on client-  { tsymbol  = '#'-  , tname    = "suspect wall"-  , tfreq    = [("suspect wall", 1)]-  , tcolor   = BrWhite-  , tcolor2  = defFG-  , talter   = 2-  , tfeature = [ RevealAs "trapped door"-               , ObscureAs "obscured wall"-               ]-  }-wallObscured = TileKind-  { tsymbol  = '#'-  , tname    = "scratched wall"-  , tfreq    = [("obscured wall", 70)]-  , tcolor   = BrWhite-  , tcolor2  = defFG-  , talter   = 5-  , tfeature = [ Embed "scratch on wall"-               , HideAs "suspect wall"-               ]-  }-wallObscuredDefaced = TileKind-  { tsymbol  = '#'-  , tname    = "defaced wall"-  , tfreq    = [ ("obscured wall", 45), ("escapeSetDark", 2)-               , ("museumSetDark", 2) ]-  , tcolor   = BrWhite-  , tcolor2  = defFG-  , talter   = 5-  , tfeature = [ Embed "obscene pictogram"-               , HideAs "suspect wall"-               ]-  }-wallObscuredFrescoed = TileKind-  { tsymbol  = '#'-  , tname    = "subtle mural"-  , tfreq    = [("obscured wall", 5), ("museumSetDark", 2)]-  , tcolor   = BrWhite-  , tcolor2  = defFG-  , talter   = 5-  , tfeature = [ Embed "subtle fresco"-               , HideAs "suspect wall"-               ]  -- a bit beneficial, but AI would loop if allowed to trigger-                  -- so no @ConsideredByAI@-  }-pillar = TileKind-  { tsymbol  = '0'-  , tname    = "construction beam"-  , tfreq    = [ ("legendLit", 100), ("legendDark", 100)-               , ("museumSetDark", 20), ("emptySetLit", 20) ]-  , tcolor   = BrCyan  -- not BrWhite, to tell from heroes-  , tcolor2  = Cyan-  , talter   = 100-  , tfeature = []-  }-pillarCache = TileKind-  { tsymbol  = '#'-  , tname    = "abandoned stash"-  , tfreq    = [ ("cachable abandoned", 20)-               , ("cache maze", 33), ("cache shuttle", 25) ]-  , tcolor   = BrBlue-  , tcolor2  = Blue-  , talter   = 5-  , tfeature = [ Embed "abandoned cache"-               , ChangeTo "cachable abandoned", ConsideredByAI ]-      -- Not explorable, but prominently placed, so hard to miss.-      -- Very beneficial, so AI eager to trigger, unless wary of traps.-  }-lampPost = TileKind-  { tsymbol  = '0'-  , tname    = "lamp post"-  , tfreq    = [("lampPostOver_0", 1)]-  , tcolor   = BrYellow-  , tcolor2  = Brown-  , talter   = 100-  , tfeature = []  -- embed something and explain how there's often-                   -- artificial ambient light in the habitats, but not in all-                   -- of them and in both cases lamps are used to provide fancy-                   -- (extra) lighting; say how low energy drain, such as-                   -- permanent ambient light, is not a problem due to tech-                   -- and also because it's a tiny fraction of what is needed-                   -- for the ecosystem/life support-  }-signboardUnread = TileKind  -- client only, indicates never used by this faction-  { tsymbol  = '0'-  , tname    = "signboard"-  , tfreq    = [("signboard unread", 1)]-  , tcolor   = BrCyan-  , tcolor2  = Cyan-  , talter   = 5-  , tfeature = [ ConsideredByAI  -- changes after use, so safe for AI-               , RevealAs "signboard"  -- to display as hidden-               ]-  }-signboardRead = TileKind-  { tsymbol  = '0'-  , tname    = "signboard"-  , tfreq    = [ ("signboard", 1), ("emptySetLit", 1)-               , ("arenaSetLit", 1), ("arenaSetDark", 1), ("museumSetDark", 1)-               , ("escapeSetDark", 1) ]-  , tcolor   = BrCyan-  , tcolor2  = Cyan-  , talter   = 5-  , tfeature = [Embed "signboard", HideAs "signboard unread"]-  }-tree = TileKind-  { tsymbol  = '0'-  , tname    = "tree"-  , tfreq    = [ ("brawlSetLit", 140), ("shootoutSetLit", 10)-               , ("huntSetLit", 10), ("escapeSetLit", 35), ("zooSetDark", 20)-               , ("treeShadeOver_0_Lit", 1) ]-  , tcolor   = BrGreen-  , tcolor2  = Green-  , talter   = 50-  , tfeature = []-  }-treeBurnt = tree-  { tname    = "burnt tree"-  , tfreq    = [("zooSetDark", 10), ("tree with fire", 30)]-  , tcolor   = BrBlack-  , tcolor2  = BrBlack-  , tfeature = Dark : tfeature tree-  }-treeBurning = tree-  { tname    = "burning tree"-  , tfreq    = [("zooSetDark", 60), ("tree with fire", 70)]-  , tcolor   = BrRed-  , tcolor2  = Red-  , talter   = 5-  , tfeature = Embed "big fire" : ChangeTo "tree with fire" : tfeature tree-      -- TODO: dousing off the tree will have more sense when it periodically-      -- explodes, hitting and lighting up the team and so betraying it-  }-rubble = TileKind-  { tsymbol  = '&'-  , tname    = "rubble pile"-  , tfreq    = [ ("rubble", 1), ("legendLit", 1), ("legendDark", 1)-               , ("stair terminal Lit", 6), ("stair terminal Dark", 6)-               , ("lift terminal Lit", 6), ("lift terminal Dark", 6)-               , ("emptySetLit", 4), ("exitSetLit", 8)-               , ("noiseSetLit", 50), ("powerSetDark", 150)-               , ("zooSetDark", 100), ("ambushSetDark", 3) ]-  , tcolor   = BrYellow-  , tcolor2  = Brown-  , talter   = 4  -- boss can dig through-  , tfeature = [OpenTo "floorAshesLit", Embed "rubble"]-      -- It's not explorable, due to not being walkable nor clear and due-      -- to being a door (@OpenTo@), which is kind of OK, because getting-      -- the item is risky and, e.g., AI doesn't attempt it.-      -- Also, AI doesn't go out of its way to clear the way for heroes.-  }-rubbleSpice = rubble-  { tfreq    = [ ("smokeClumpOver_f_Lit", 1), ("smokeClumpOver_f_Dark", 1)-               , ("rubbleOrWaste_Lit", 1), ("rubbleOrWaste_Dark", 1)-               , ("cache deposit", 33), ("cachable deposit", 80) ]-  , tfeature = Spice : tfeature rubble-  }-doorTrapped = TileKind-  { tsymbol  = '+'-  , tname    = "trapped door"-  , tfreq    = [("trapped door", 1)]-  , tcolor   = BrRed-  , tcolor2  = Red-  , talter   = 2-  , tfeature = [ Embed "doorway trap"-               , OpenTo "open door"-               , HideAs "suspect wall"-               ]-  }-doorClosed = TileKind-  { tsymbol  = '+'-  , tname    = "closed door"-  , tfreq    = [("legendLit", 100), ("legendDark", 100), ("closed door", 1)]-  , tcolor   = Brown-  , tcolor2  = BrBlack-  , talter   = 2-  , tfeature = [OpenTo "open door"]  -- never hidden-  }-stairsUp = TileKind-  { tsymbol  = '<'-  , tname    = "staircase up"-  , tfreq    = [("staircase up", 9), ("ordinary staircase up", 1)]-  , tcolor   = BrWhite-  , tcolor2  = defFG-  , talter   = 0  -- very easy stairs, unlike all others-  , tfeature = [Embed "staircase up", ConsideredByAI]-  }-stairsTrappedUp = TileKind-  { tsymbol  = '<'-  , tname    = "windy staircase up"-  , tfreq    = [("staircase up", 1)]-  , tcolor   = BrRed-  , tcolor2  = Red-  , talter   = talterForStairs-  , tfeature = [ Embed "staircase up", Embed "staircase trap up"-               , ConsideredByAI, ChangeTo "ordinary staircase up" ]-                 -- AI uses despite the trap; exploration more important-  }-stairsOutdoorUp = stairsUp-  { tname    = "signpost pointing backward"-  , tfreq    = [("staircase outdoor up", 1)]-  , talter   = talterForStairs-  }-stairsGatedUp = stairsUp-  { tname    = "gated staircase up"-  , tfreq    = [("gated staircase up", 1)]-  , talter   = talterForStairs + 2  -- animals and bosses can't use-  }-stairsDown = TileKind-  { tsymbol  = '>'-  , tname    = "staircase down"-  , tfreq    = [("staircase down", 9), ("ordinary staircase down", 1)]-  , tcolor   = BrWhite-  , tcolor2  = defFG-  , talter   = 0  -- very easy stairs, unlike all others-  , tfeature = [Embed "staircase down", ConsideredByAI]-  }-stairsTrappedDown = TileKind-  { tsymbol  = '>'-  , tname    = "crooked staircase down"-  , tfreq    = [("staircase down", 1)]-  , tcolor   = BrRed-  , tcolor2  = Red-  , talter   = talterForStairs-  , tfeature = [ Embed "staircase down", Embed "staircase trap down"-               , ConsideredByAI, ChangeTo "ordinary staircase down" ]-  }-stairsOutdoorDown = stairsDown-  { tname    = "signpost pointing forward"-  , tfreq    = [("staircase outdoor down", 1)]-  , talter   = talterForStairs-  }-stairsGatedDown = stairsDown-  { tname    = "gated staircase down"-  , tfreq    = [("gated staircase down", 1)]-  , talter   = talterForStairs + 2  -- animals and bosses can't use-  }-escapeUp = TileKind-  { tsymbol  = '<'-  , tname    = "exit hatch up"-  , tfreq    = [("legendLit", 1), ("legendDark", 1), ("escape up", 1)]-  , tcolor   = BrYellow-  , tcolor2  = BrYellow-  , talter   = 0  -- anybody can escape (or guard escape)-  , tfeature = [Embed "escape", ConsideredByAI]-  }-escapeDown = TileKind-  { tsymbol  = '>'-  , tname    = "exit trapdoor down"-  , tfreq    = [("legendLit", 1), ("legendDark", 1), ("escape down", 1)]-  , tcolor   = BrYellow-  , tcolor2  = BrYellow-  , talter   = 0  -- anybody can escape (or guard escape)-  , tfeature = [Embed "escape", ConsideredByAI]-  }-escapeOutdoorDown = escapeDown-  { tname    = "exit back to town"-  , tfreq    = [("escape outdoor down", 1)]-  }---- *** Clear--wallGlass = TileKind-  { tsymbol  = '%'-  , tname    = "transparent polymer wall"-  , tfreq    = [("legendLit", 1), ("legendDark", 1), ("museumSetDark", 8)]-  , tcolor   = BrBlue-  , tcolor2  = Blue-  , talter   = 10-  , tfeature = [BuildAs "closed door", Clear]-  }-wallGlassSpice = wallGlass-  { tfreq    = [ ("rectWindowsOver_%", 20)-               , ("cache jewelry", 66), ("cachable jewelry", 80) ]-  , tfeature = Spice : tfeature wallGlass-  }-pillarIce = TileKind-  { tsymbol  = '^'-  , tname    = "ice buildup"-  , tfreq    = [ ("legendLit", 1), ("legendDark", 1), ("noiseSetLit", 300)-               , ("brawlSetLit", 20), ("lift terminal Dark", 4) ]-                 -- ice only in dark staircases-  , tcolor   = BrBlue-  , tcolor2  = Blue-  , talter   = 4  -- boss can dig through-  , tfeature = [Clear, Embed "frost", OpenTo "shallow water Lit"]-      -- Is door, due to @OpenTo@, so is not explorable, but it's OK, because-      -- it doesn't generate items nor clues. This saves on the need to-      -- get each ice pillar into sight range when exploring level.-  }-pulpit = TileKind-  { tsymbol  = '%'-  , tname    = "VR booth"-  , tfreq    = [("pulpit", 1)]-  , tcolor   = BrYellow-  , tcolor2  = Brown-  , talter   = 5-  , tfeature = [Clear, Embed "pulpit"]-                 -- mixed blessing, so AI ignores, saved for player fun-  }-bush = TileKind-  { tsymbol  = '%'-  , tname    = "bush"-  , tfreq    = [ ("bush Lit", 1), ("arenaSetLit", 10), ("shootoutSetLit", 30)-               , ("huntSetLit", 30), ("escapeSetLit", 40), ("zooSetDark", 100)-               , ("bushClumpOver_f_Lit", 1), ("pumpsOver_f_Lit", 1)-               , ("lift terminal Lit", 4) ]-  , tcolor   = BrGreen-  , tcolor2  = Green-  , talter   = 10-  , tfeature = [Clear]-  }-bushBurnt = bush-  { tname    = "burnt bush"-  , tfreq    = [ ("battleSetDark", 30), ("ambushSetDark", 3), ("zooSetDark", 50)-               , ("bush with fire", 70) ]-  , tcolor   = BrBlack-  , tcolor2  = BrBlack-  , tfeature = Dark : tfeature bush-  }-bushBurning = bush-  { tname    = "burning bush"-  , tfreq    = [ ("ambushSetDark", 10), ("zooSetDark", 300)-               , ("bush with fire", 30) ]-  , tcolor   = BrRed-  , tcolor2  = Red-  , talter   = 5-  , tfeature = Embed "small fire" : ChangeTo "bush with fire" : tfeature bush-  }---- ** Walkable---- *** Not clear--fog = TileKind-  { tsymbol  = ';'-  , tname    = "faint fog"-  , tfreq    = [ ("fog Lit", 1), ("emptySetLit", 50), ("noiseSetLit", 120)-               , ("shootoutSetLit", 30), ("huntSetLit", 30)-               , ("fogClumpOver_f_Lit", 60), ("fogClumpOver_f_Dark", 60)-               , ("lift terminal Lit", 40) ]-      -- lit fog is OK for shootout, because LOS is mutual, as opposed-      -- to dark fog, and so camper has little advantage, especially-      -- on big maps, where he doesn't know on which side of fog patch to hide-  , tcolor   = BrCyan-  , tcolor2  = Cyan-  , talter   = 0-  , tfeature = [Walkable, NoItem, OftenActor]-  }-fogDark = fog-  { tname    = "thick fog"-  , tfreq    = [("escapeSetDark", 50), ("lift terminal Dark", 40)]-  , tfeature = Dark : tfeature fog-  }-smoke = TileKind-  { tsymbol  = ';'-  , tname    = "billowing smoke"-  , tfreq    = [ ("smoke Lit", 1), ("labTrailLit", 1)-               , ("stair terminal Lit", 2), ("lift terminal Lit", 6)-               , ("smokeClumpOver_f_Lit", 3), ("smokeClumpOver_f_Dark", 3)-               , ("exitSetLit", 20), ("ambushSetDark", 20) ]-  , tcolor   = Brown-  , tcolor2  = BrBlack-  , talter   = 0-  , tfeature = [Walkable, NoItem]  -- not dark, embers-  }-smokeDark = smoke-  { tname    = "lingering smoke"-  , tfreq    = [ ("powerSetDark", 100)-               , ("zooSetDark", 20), ("ambushSetDark", 40), ("battleSetDark", 5)-               , ("stair terminal Dark", 2), ("lift terminal Dark", 6) ]-  , tfeature = Dark : tfeature smoke-  }---- *** Clear--doorOpen = TileKind-  { tsymbol  = '\''-  , tname    = "open door"-  , tfreq    = [("legendLit", 100), ("legendDark", 100), ("open door", 1)]-  , tcolor   = Brown-  , tcolor2  = BrBlack-  , talter   = 4-  , tfeature = [ Walkable, Clear, NoItem, NoActor-               , CloseTo "closed door"-               ]-  }-floorCorridor = TileKind-  { tsymbol  = floorSymbol-  , tname    = "floor"-  , tfreq    = [("floorCorridorLit", 1)]-  , tcolor   = BrWhite-  , tcolor2  = defFG-  , talter   = 0-  , tfeature = [Walkable, Clear]-  }-floorArena = floorCorridor-  { tfreq    = [ ("floorArenaLit", 1), ("arenaSetLit", 200)-               , ("museumSetLit", 400), ("zooSetLit", 600) ]-  }-floorDamp = floorArena-  { tname    = "damp floor"-  , tfreq    = [ ("noiseSetLit", 600), ("emptySetLit", 900)-               , ("damp floor Lit", 1)-               , ("stair terminal Lit", 20), ("lift terminal Lit", 6) ]-  }-floorDirt = floorArena-  { tname    = "dirt"-  , tfreq    = [ ("brawlSetLit", 1000), ("shootoutSetLit", 1000)-               , ("huntSetLit", 1000), ("escapeSetLit", 1000)-               , ("ambushSetLit", 1000), ("battleSetLit", 1000)-               , ("dirt Lit", 1) ]-  }-floorDirtSpice = floorDirt-  { tfreq    = [ ("treeShadeOver_s_Lit", 1), ("bushClumpOver_f_Lit", 1)-               , ("pumpsOver_f_Lit", 3) ]-  , tfeature = Spice : tfeature floorDirt-  }-floorActor = floorArena-  { tfreq    = [("floorActorLit", 1)]-  , tfeature = OftenActor : tfeature floorArena-  }-floorActorItem = floorActor-  { tfreq    = [("floorActorItem", 1), ("legendLit", 100)]-  , tfeature = VeryOftenItem : tfeature floorActor-  }-floorAshes = floorActor-  { tfreq    = [ ("smokeClumpOver_f_Lit", 2), ("smokeClumpOver_f_Dark", 2)-               , ("floorAshesLit", 1), ("floorAshesDark", 1) ]-  , tname    = "dirt and ash pile"-  , tcolor   = Brown-  , tcolor2  = Brown-  }-shallowWater = TileKind-  { tsymbol  = '~'-  , tname    = "water puddle"-  , tfreq    = [ ("aquatic", 1), ("shallow water Lit", 1), ("legendLit", 100)-               , ("emptySetLit", 5), ("noiseSetLit", 30), ("shootoutSetLit", 5)-               , ("huntSetLit", 250), ("lift terminal Lit", 4) ]-  , tcolor   = BrCyan-  , tcolor2  = Cyan-  , talter   = 0-  , tfeature = Embed "shallow water" : tfeature floorActor-  }-shallowWaterSpice = shallowWater-  { tfreq    = [ ("fogClumpOver_f_Lit", 40), ("pumpsOver_f_Lit", 3)-               , ("rubbleOrWaste_Lit", 1) ]-  , tfeature = Spice : tfeature shallowWater-  }-shallowWater2 = shallowWater-  { tname    = "water pool"-  , tfreq    = [("poolOver_~_Lit", 1)]-  }-floorRed = floorCorridor-  { tname    = "emergency walkway"-  , tfreq    = [ ("trailLit", 70), ("safeTrailLit", 70)-               , ("lift terminal Lit", 6), ("lift terminal Dark", 6) ]-  , tcolor   = BrRed-  , tcolor2  = Red-  , tfeature = [Embed "straight path", Trail, Walkable, Clear]-  }-floorBlue = floorRed-  { tname    = "frozen path"-  , tfreq    = [("trailLit", 100), ("frozen path", 1)]-  , tcolor   = BrBlue-  , tcolor2  = Blue-  , tfeature = [Embed "frozen ground", Trail, Walkable, Clear]-  }-floorGreen = floorRed-  { tname    = "greenery trail"-  , tfreq    = [("trailLit", 70), ("safeTrailLit", 70)]-  , tcolor   = BrGreen-  , tcolor2  = Green-  }-floorBrown = floorRed-  { tname    = "transport route"-  , tfreq    = [ ("trailLit", 50), ("safeTrailLit", 50)-               , ("transport route", 1) ]-  , tcolor   = BrMagenta-  , tcolor2  = Magenta-  }-floorArenaShade = floorActor-  { tname    = "shaded ground"-  , tfreq    = [("shaded ground", 1), ("treeShadeOver_s_Lit", 2)]-  , tcolor   = BrYellow  -- match others, even though no lit counterpart-  , tcolor2  = BrBlack-  , tfeature = Dark : NoItem : tfeature floorActor-  }---- * Allure-specific---- ** Not walkable---- *** Not clear--oriel = TileKind-  { tsymbol  = '%'  -- story-wise it's transparent, hence the symbol-  , tname    = "oriel"-  , tfreq    = [ ("oriels fence", 15)-               , ("airlock fence", 5), ("empty airlock fence", 5) ]-  , tcolor   = White-  , tcolor2  = Black-  , talter   = 5-  , tfeature = [Embed "black starry sky", Dark]-  }-outerHullWall = basicOuterFence-  { tname    = "outer hull wall"-  , tfreq    = [ ("basic outer fence", 1), ("oriels fence", 85)-               , ("airlock fence", 40), ("empty airlock fence", 40) ]-  }-rubbleBurning = TileKind  -- present in "emptySetLit" for early light source-  { tsymbol  = '&'-  , tname    = "burning installation"-  , tfreq    = [ ("emptySetLit", 2), ("powerSetDark", 20)-               , ("ambushSetDark", 15), ("zooSetDark", 30)-               , ("stair terminal Lit", 4), ("stair terminal Dark", 4)-               , ("lift terminal Lit", 4), ("lift terminal Dark", 4) ]-  , tcolor   = BrRed-  , tcolor2  = Red-  , talter   = 4  -- boss can dig through-  , tfeature = [ChangeTo "rubble", Embed "big fire"]-  }-rubbleBurningSpice = rubbleBurning-  { tfreq    = [ ("smokeClumpOver_f_Lit", 1), ("smokeClumpOver_f_Dark", 1)-               , ("cache deposit", 33) ]-  , tfeature = Spice : tfeature rubbleBurning-  }-wallOpenable = bedrock-  { tfreq    = [("openableWall", 1)]-  , tfeature = [BuildAs "closed door"]-  }-wallObscuredSafety = TileKind-  { tsymbol  = '#'-  , tname    = "safety procedures board"-  , tfreq    = [("obscured wall", 5), ("exitSetLit", 1)]-  , tcolor   = BrWhite-  , tcolor2  = defFG-  , talter   = 5-  , tfeature = [ Embed "ruined first aid kit"-               , HideAs "suspect wall"-               ]-  }-wallObscured3dBillboard = TileKind-  { tsymbol  = '#'-  , tname    = "3D billboard"-  , tfreq    = [("obscured wall", 25)]-  , tcolor   = BrWhite-  , tcolor2  = defFG-  , talter   = 5-  , tfeature = [ Embed "3D display"-               , HideAs "suspect wall"-               ]-  }-liftShaft = pillar-  { tname    = "lift shaft"-  , tfreq    = [("lift shaft", 1)]-  }-rock = pillar-  { tname    = "rock"-  , tfreq    = [("arenaSetLit", 4), ("arenaSetDark", 4), ("brawlSetLit", 30)]-  }-pillarCache2 = pillarCache-  { tname    = "rack of deposit boxes"-  , tfreq    = [ ("cachable deposit", 20), ("cache deposit", 33)-               , ("stair terminal Lit", 1), ("stair terminal Dark", 1) ]-  , tfeature = [ Embed "deposit box"-               , ChangeTo "cachable deposit", ConsideredByAI ]-  }-pillarCache3 = pillarCache-  { tname    = "jewelry display"-  , tfreq    = [ ("cachable jewelry", 20), ("cache jewelry", 33)-               , ("museumSetDark", 2) ]-  , tfeature = [ Embed "jewelry case", Embed "jewelry display trap"-               , ChangeTo "cachable jewelry", ConsideredByAI ]-  }-stairsDecontaminatingUp = stairsUp-  { tname    = "decontaminating staircase up"-  , tfreq    = [("decontaminating staircase up", 1)]-  , tcolor   = BrBlue-  , tcolor2  = Blue-  , talter   = talterForStairs-  , tfeature = Embed "decontamination chamber" : tfeature stairsUp-  }-stairsWelded = stairsUp-  { tname    = "staircase up welded shut"-  , tfreq    = [("welded staircase up", 1)]-  , tcolor   = BrMagenta-  , tcolor2  = Magenta-  , talter   = talterForStairs + 3  -- gear needed-  , tfeature = [ ConsideredByAI, ChangeTo "ordinary staircase up"-               , Embed "crude weld" ]-  }-stairsLiftUp = stairsUp-  { tname    = "lift up"-  , tfreq    = [("staircase lift up", 9), ("ordinary lift up", 1)]-  , talter   = talterForStairs-  , tcolor   = BrCyan-  , tcolor2  = Cyan-  , tfeature = [Embed "lift up", ConsideredByAI]-  }-stairsLiftTrappedUp = stairsTrappedUp-  { tname    = "corroded lift up"-  , tfreq    = [("staircase lift up", 1)]-  , tcolor   = BrBlue-  , tcolor2  = Blue-  , tfeature = [ Embed "lift up", Embed "lift trap"-               , ConsideredByAI, ChangeTo "ordinary lift up" ]-                 -- AI uses despite the trap; exploration more important-  }-stairsLiftGatedUp = stairsLiftUp-  { tname    = "manually opened lift up"-  , tfreq    = [("gated lift up", 1)]-  , talter   = talterForStairs + 2  -- animals and bosses can't use-  }-stairsLiftDecontaminatingUp = stairsLiftUp-  { tname    = "decontaminating lift up"-  , tfreq    = [("decontaminating lift up", 1)]-  , tcolor   = BrBlue-  , tcolor2  = Blue-  , tfeature = Embed "decontamination chamber" : tfeature stairsLiftUp-  }-stairsLiftWelded = stairsLiftUp-  { tname    = "lift up welded shut"-  , tfreq    = [("welded lift up", 1)]-  , tcolor   = BrMagenta-  , tcolor2  = Magenta-  , talter   = talterForStairs + 3  -- gear needed-  , tfeature = [ ConsideredByAI, ChangeTo "ordinary lift up"-               , Embed "crude weld" ]-  }-stairsDecontaminatingDown = stairsDown-  { tname    = "decontaminating staircase down"-  , tfreq    = [("decontaminating staircase down", 1)]-  , tcolor   = BrBlue-  , tcolor2  = Blue-  , talter   = talterForStairs-  , tfeature = Embed "decontamination chamber" : tfeature stairsDown-  }-stairsLiftDown = stairsDown-  { tname    = "lift down"-  , tfreq    = [("staircase lift down", 9), ("ordinary lift down", 1)]-  , tcolor   = BrCyan-  , tcolor2  = Cyan-  , talter   = talterForStairs-  , tfeature = [Embed "lift down", ConsideredByAI]-  }-stairsLiftTrappedDown = stairsTrappedDown-  { tname    = "corroded lift down"-  , tfreq    = [("staircase lift down", 1)]-  , tcolor   = BrBlue-  , tcolor2  = Blue-  , tfeature = [ Embed "lift down", Embed "lift trap"-               , ConsideredByAI, ChangeTo "ordinary lift down" ]-  }-stairsLiftGatedDown = stairsLiftDown-  { tname    = "manually opened lift down"-  , tfreq    = [("gated lift down", 1)]-  , talter   = talterForStairs + 2  -- animals and bosses can't use-  }-stairsLiftDecontaminatingDown = stairsLiftDown-  { tname    = "decontaminating lift down"-  , tfreq    = [("decontaminating lift down", 1)]-  , tcolor   = BrBlue-  , tcolor2  = Blue-  , tfeature = Embed "decontamination chamber" : tfeature stairsLiftDown-  }-escapeSpaceshipDown = escapeDown-  { tname    = "airlock to a shuttle"-  , tfreq    = [("escape spaceship down", 1), ("airlock fence", 3)]-  }-emptyAirlock = escapeUp-  { tname    = "empty airlock"-  , tfreq    = [ ("airlock fence", 2), ("empty airlock fence", 7)-               , ("emptySetLit", 2), ("ambushSetDark", 7) ]-                   -- not in exitSetLit; space can't be seen-  , tcolor   = BrBlack-  , tcolor2  = BrBlack-  , tfeature = [Embed "disengaged docking"]-  }-reinforcedWall = TileKind-  { tsymbol  = '#'-  , tname    = "reinforced wall"-  , tfreq    = [("reinforced wall", 1), ("rogueSet", 15), ("exitSetLit", 20)]-  , tcolor   = White-  , tcolor2  = BrBlack-  , talter   = 100-  , tfeature = []-  }-reinforcedWallSpice = reinforcedWall-  { tfreq    = [ ("doorlessWallOver_#", 20)-               , ("cache maze", 66), ("cachable abandoned", 80) ]-  , tfeature = Spice : tfeature reinforcedWall-  }-wallShuttle = bedrock-  { tname    = "shuttle hull"-  , tfreq    = [("shuttle hull", 1)]-  , tfeature = [Embed "shuttle hardware"]-  }-wallShuttleSpice = wallShuttle-  { tfreq    = [("cache shuttle", 75)]-  , tfeature = Spice : tfeature wallShuttle-  }---- *** Clear--machineWall = TileKind-  { tsymbol  = '%'-  , tname    = "hardware rack"-  , tfreq    = [ ("hardware rack", 1)-               , ("rogueSet", 25), ("noiseSetLit", 250), ("powerSetDark", 250)-               , ("exitSetLit", 30)-               , ("lift terminal Lit", 40), ("lift terminal Dark", 40) ]-  , tcolor   = White-  , tcolor2  = BrBlack-  , talter   = 100-  , tfeature = [Clear]-  }-machineWallSpice = machineWall-  { tfreq    = [("doorlessMachineryOver_#", 1)]-  , tfeature = Spice : tfeature machineWall-  }---- ** Walkable---- *** Clear--floorOily = floorArena-  { tname    = "oily floor"-  , tfreq    = [ ("powerSetLit", 600), ("exitSetLit", 900)-               , ("oily floor Lit", 1), ("rubbleOrWaste_Lit", 1)-               , ("oilOver_o_Lit", 4) ]-  }-oilSpill = TileKind-  { tsymbol  = '~'-  , tname    = "oil spill"-  , tfreq    = [ ("powerSetDark", 35), ("exitSetLit", 1)-               , ("ambushSetDark", 20), ("oil spill", 1) ]-  , tcolor   = BrYellow-  , tcolor2  = BrGreen-  , talter   = 0-  , tfeature = Embed "machine oil" : tfeature floorActor-  }-oilSpillSpice = oilSpill-  { tfreq    = [ ("rubbleOrWaste_Lit", 1), ("rubbleOrWaste_Dark", 1)-               , ("oilOver_o_Lit", 1), ("oilOver_o_Dark", 1) ]-  , tfeature = Spice : tfeature oilSpill-  }-floorWindow = floorArena-  { tsymbol  = ' '  -- story-wise it's transparent, hence the symbol-  , tname    = "floor window"-  , tfreq    = [("emptySetLit", 6)]-  , tcolor   = defFG-  , tcolor2  = defFG-  , tfeature = Embed "black starry sky" : tfeature floorCorridor-  }---- * Helper functions--makeDark :: TileKind -> TileKind-makeDark k = let darkText :: GroupName TileKind -> GroupName TileKind-                 darkText t = maybe t (toGroupName . (<> "Dark"))-                              $ T.stripSuffix "Lit" $ fromGroupName 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 (BuildAs t) = Just $ BuildAs $ darkText t-                 darkFeat (RevealAs t) = Just $ RevealAs $ darkText t-                 darkFeat (ObscureAs t) = Just $ ObscureAs $ darkText t+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history)+-- This file is a part of the computer game Allure of the Stars+-- and is released under the terms of the GNU Affero General Public License.+-- For license and copyright information, see the file LICENSE.+--+-- | Definitions of tile kinds. Every terrain tile in the game is+-- an instantiated tile kind.+module Content.TileKind+  ( -- * Group name patterns+    -- ** Used in CaveKind and perhaps elsewhere.+    pattern FILLER_WALL, pattern FLOOR_CORRIDOR_LIT, pattern FLOOR_CORRIDOR_DARK, pattern TRAIL_LIT, pattern SAFE_TRAIL_LIT, pattern LAB_TRAIL_LIT, pattern DAMP_FLOOR_LIT, pattern DAMP_FLOOR_DARK, pattern DIRT_LIT, pattern DIRT_DARK, pattern FLOOR_ARENA_LIT, pattern FLOOR_ARENA_DARK+  , pattern HABITAT_CONTAINMENT_WALL, pattern TRANSPORT_ROUTE, pattern ORIELS_FENCE, pattern AIRLOCK_FENCE, pattern EMPTY_AIRLOCK_FENCE, pattern OPENABLE_WALL, pattern TRAPPABLE_WALL, pattern OILY_FLOOR_LIT, pattern OILY_FLOOR_DARK+  , pattern EMPTY_SET_LIT, pattern EMPTY_SET_DARK, pattern NOISE_SET_LIT, pattern POWER_SET_LIT, pattern POWER_SET_DARK, pattern BATTLE_SET_LIT, pattern BATTLE_SET_DARK, pattern BRAWL_SET_LIT, pattern SHOOTOUT_SET_LIT, pattern ZOO_SET_LIT, pattern ZOO_SET_DARK, pattern ESCAPE_SET_LIT, pattern ESCAPE_SET_DARK, pattern AMBUSH_SET_LIT, pattern AMBUSH_SET_DARK, pattern ARENA_SET_LIT, pattern ARENA_SET_DARK+  , pattern ROGUE_SET, pattern MUSEUM_SET_LIT, pattern MUSEUM_SET_DARK, pattern HUNT_SET_LIT, pattern EXIT_SET_LIT+    -- ** Used in PlaceKind, but not in CaveKind.+  , pattern TREE_SHADE_WALKABLE_LIT, pattern TREE_SHADE_WALKABLE_DARK, pattern SMOKE_CLUMP_LIT, pattern SMOKE_CLUMP_DARK, pattern BUSH_CLUMP_LIT, pattern BUSH_CLUMP_DARK, pattern FOG_CLUMP_LIT, pattern FOG_CLUMP_DARK, pattern STAIR_TERMINAL_LIT, pattern STAIR_TERMINAL_DARK, pattern SIGNBOARD, pattern STAIRCASE_UP, pattern ORDINARY_STAIRCASE_UP, pattern STAIRCASE_OUTDOOR_UP, pattern GATED_STAIRCASE_UP, pattern STAIRCASE_DOWN, pattern ORDINARY_STAIRCASE_DOWN, pattern STAIRCASE_OUTDOOR_DOWN, pattern GATED_STAIRCASE_DOWN, pattern ESCAPE_UP, pattern ESCAPE_DOWN, pattern ESCAPE_OUTDOOR_DOWN+  , pattern S_LAMP_POST, pattern S_TREE_LIT, pattern S_TREE_DARK, pattern S_PULPIT, pattern S_BUSH_LIT, pattern S_FOG_LIT, pattern S_SMOKE_LIT, pattern S_FLOOR_ACTOR_LIT, pattern S_FLOOR_ACTOR_DARK, pattern S_FLOOR_ASHES_LIT, pattern S_FLOOR_ASHES_DARK, pattern S_SHADED_GROUND+  , pattern BUSH_GROVE_LIT, pattern BUSH_GROVE_DARK, pattern UNDERBRUSH_CLUMP_LIT, pattern UNDERBRUSH_CLUMP_DARK, pattern ASHES_SMOKE_LIT, pattern ASHES_SMOKE_DARK, pattern RECT_WINDOWS, pattern DOORLESS_MACHINERY, pattern PUMPS_LIT, pattern PUMPS_DARK, pattern DOORLESS_WALL, pattern OIL_RESIDUE_LIT, pattern OIL_RESIDUE_DARK, pattern LIFT_TERMINAL_LIT, pattern LIFT_TERMINAL_DARK, pattern STAIRCASE_LIFT_UP, pattern STAIRCASE_LIFT_DOWN, pattern GATED_LIFT_UP, pattern GATED_LIFT_DOWN, pattern DECON_STAIRCASE_UP, pattern DECON_STAIRCASE_DOWN, pattern DECON_LIFT_UP, pattern DECON_LIFT_DOWN, pattern WELDED_STAIRCASE_UP, pattern WELDED_LIFT_UP, pattern ESCAPE_SPACESHIP_DOWN, pattern ORDINARY_LIFT_UP, pattern ORDINARY_LIFT_DOWN, pattern RUBBLE_OR_WASTE_LIT, pattern RUBBLE_OR_WASTE_DARK, pattern CACHE_DEPOSIT, pattern CACHE_JEWELRY, pattern CACHE_MAZE, pattern CACHE_SHUTTLE, pattern TRAPPED_DOOR, pattern FLOOR_ACTOR_ITEM, pattern FLOOR_ACTOR_ITEM_LIT, pattern STUCK_DOOR, pattern BARREL+  , pattern S_POOL_LIT, pattern S_POOL_DARK, pattern S_OIL_SPILL, pattern S_FROZEN_PATH, pattern S_LIFT_SHAFT, pattern S_REINFORCED_WALL, pattern S_SHUTTLE_HULL, pattern S_HARDWARE_RACK, pattern S_STAIRCASE_TRAP_DOWN_OIL, pattern S_UNDERBRUSH_LIT, pattern S_UNDERBRUSH_DARK+  , groupNamesSingleton, groupNames+    -- * Content+  , content+  ) where++import Prelude ()++import Game.LambdaHack.Core.Prelude++import qualified Data.Text as T++import Content.ItemKindActor+import Content.ItemKindBlast+import Content.ItemKindEmbed+import Content.ItemKindOrgan+import Game.LambdaHack.Content.TileKind+import Game.LambdaHack.Definition.Color+import Game.LambdaHack.Definition.Defs++-- * Group name patterns++-- Warning, many of these are also sythesized, so typos can happen.++groupNamesSingleton :: [GroupName TileKind]+groupNamesSingleton =+       [S_LAMP_POST, S_TREE_LIT, S_TREE_DARK, S_PULPIT, S_BUSH_LIT, S_FOG_LIT, S_SMOKE_LIT, S_FLOOR_ACTOR_LIT, S_FLOOR_ACTOR_DARK, S_FLOOR_ASHES_LIT, S_FLOOR_ASHES_DARK, S_SHADED_GROUND]+    ++ [S_POOL_LIT, S_POOL_DARK, S_OIL_SPILL, S_FROZEN_PATH, S_LIFT_SHAFT, S_REINFORCED_WALL, S_SHUTTLE_HULL, S_HARDWARE_RACK]+    ++ [S_RUBBLE_PILE, S_SHALLOW_WATER_LIT, S_SIGNBOARD_UNREAD]+    ++ [S_SUSPECT_WALL, S_CLOSED_DOOR, S_OPEN_DOOR, S_STAIRCASE_TRAP_DOWN_OIL, S_BURNING_INSTALLATION, S_BURNING_TREE, S_BURNING_BUSH, S_BURNING_UNDERBRUSH, S_BURNING_OIL, S_UNDERBRUSH_LIT, S_UNDERBRUSH_DARK]+    ++ [S_BUSH_DARK, S_SHALLOW_WATER_DARK]++-- ** Used in PlaceKind, but not in CaveKind.+pattern S_LAMP_POST, S_TREE_LIT, S_TREE_DARK, S_PULPIT, S_BUSH_LIT, S_FOG_LIT, S_SMOKE_LIT, S_FLOOR_ACTOR_LIT, S_FLOOR_ACTOR_DARK, S_FLOOR_ASHES_LIT, S_FLOOR_ASHES_DARK, S_SHADED_GROUND :: GroupName TileKind++-- ** Allure-specific+pattern S_POOL_LIT, S_POOL_DARK, S_OIL_SPILL, S_FROZEN_PATH, S_LIFT_SHAFT, S_REINFORCED_WALL, S_SHUTTLE_HULL, S_HARDWARE_RACK :: GroupName TileKind++-- ** Used only internally in other TileKind definitions or never used.+pattern S_RUBBLE_PILE, S_SHALLOW_WATER_LIT, S_SIGNBOARD_UNREAD :: GroupName TileKind++-- ** Allure-specific+pattern S_SUSPECT_WALL, S_CLOSED_DOOR, S_OPEN_DOOR, S_STAIRCASE_TRAP_DOWN_OIL, S_BURNING_INSTALLATION, S_BURNING_TREE, S_BURNING_BUSH, S_BURNING_UNDERBRUSH, S_BURNING_OIL, S_UNDERBRUSH_LIT, S_UNDERBRUSH_DARK :: GroupName TileKind++-- * Not used, but needed, because auto-generated. Singletons.+pattern S_BUSH_DARK, S_SHALLOW_WATER_DARK :: GroupName TileKind++-- TODO: if we stick to the current system of generating extra kinds and their+-- group names, let's also add the generated group names to @groupNames@.+groupNames :: [GroupName TileKind]+groupNames =+       [AQUATIC]+    ++ [FILLER_WALL, FLOOR_CORRIDOR_LIT, FLOOR_CORRIDOR_DARK, TRAIL_LIT, SAFE_TRAIL_LIT, LAB_TRAIL_LIT, DAMP_FLOOR_LIT, DAMP_FLOOR_DARK, DIRT_LIT, DIRT_DARK, FLOOR_ARENA_LIT, FLOOR_ARENA_DARK]+    ++ [HABITAT_CONTAINMENT_WALL, TRANSPORT_ROUTE, ORIELS_FENCE, AIRLOCK_FENCE, EMPTY_AIRLOCK_FENCE, OPENABLE_WALL, TRAPPABLE_WALL, OILY_FLOOR_LIT, OILY_FLOOR_DARK]+    ++ [EMPTY_SET_LIT, EMPTY_SET_DARK, NOISE_SET_LIT, POWER_SET_LIT, POWER_SET_DARK, BATTLE_SET_LIT, BATTLE_SET_DARK, BRAWL_SET_LIT, SHOOTOUT_SET_LIT, ZOO_SET_LIT, ZOO_SET_DARK, ESCAPE_SET_LIT, ESCAPE_SET_DARK, AMBUSH_SET_LIT, AMBUSH_SET_DARK, ARENA_SET_LIT, ARENA_SET_DARK]+    ++ [ROGUE_SET, MUSEUM_SET_LIT, MUSEUM_SET_DARK, HUNT_SET_LIT, EXIT_SET_LIT]+    ++ [TREE_SHADE_WALKABLE_LIT, TREE_SHADE_WALKABLE_DARK, SMOKE_CLUMP_LIT, SMOKE_CLUMP_DARK, BUSH_CLUMP_LIT, BUSH_CLUMP_DARK, FOG_CLUMP_LIT, FOG_CLUMP_DARK, STAIR_TERMINAL_LIT, STAIR_TERMINAL_DARK, SIGNBOARD, STAIRCASE_UP, ORDINARY_STAIRCASE_UP, STAIRCASE_OUTDOOR_UP, GATED_STAIRCASE_UP, STAIRCASE_DOWN, ORDINARY_STAIRCASE_DOWN, STAIRCASE_OUTDOOR_DOWN, GATED_STAIRCASE_DOWN, ESCAPE_UP, ESCAPE_DOWN, ESCAPE_OUTDOOR_DOWN]+    ++ [BUSH_GROVE_LIT, BUSH_GROVE_DARK, UNDERBRUSH_CLUMP_LIT, UNDERBRUSH_CLUMP_DARK, ASHES_SMOKE_LIT, ASHES_SMOKE_DARK, RECT_WINDOWS, DOORLESS_MACHINERY, PUMPS_LIT, PUMPS_DARK, DOORLESS_WALL, OIL_RESIDUE_LIT, OIL_RESIDUE_DARK, LIFT_TERMINAL_LIT, LIFT_TERMINAL_DARK, STAIRCASE_LIFT_UP, STAIRCASE_LIFT_DOWN, GATED_LIFT_UP, GATED_LIFT_DOWN, DECON_STAIRCASE_UP, DECON_STAIRCASE_DOWN, DECON_LIFT_UP, DECON_LIFT_DOWN, WELDED_STAIRCASE_UP, WELDED_LIFT_UP, ESCAPE_SPACESHIP_DOWN, ORDINARY_LIFT_UP, ORDINARY_LIFT_DOWN, RUBBLE_OR_WASTE_LIT, RUBBLE_OR_WASTE_DARK, CACHE_DEPOSIT, CACHE_JEWELRY, CACHE_MAZE, CACHE_SHUTTLE, TRAPPED_DOOR, FLOOR_ACTOR_ITEM, FLOOR_ACTOR_ITEM_LIT, STUCK_DOOR, BARREL]+    ++ [TREE_BURNING_OR_NOT, BUSH_BURNING_OR_NOT]+    ++ [OBSCURED_WALL, CACHE_DEPOSIT_OR_NOT, CACHE_DEPOSIT_BREACHED, CACHE_JEWELRY_OR_NOT, CACHE_JEWELRY_TRAPPED_OR_NOT, CACHE_ABANDONED_OR_NOT, RUBBLE_BURNING_OR_NOT]+    ++ [BRAWL_SET_DARK, NOISE_SET_DARK, SHOOTOUT_SET_DARK, EXIT_SET_DARK, HUNT_SET_DARK, FLOOR_ACTOR_ITEM_DARK]++pattern FILLER_WALL, FLOOR_CORRIDOR_LIT, FLOOR_CORRIDOR_DARK, TRAIL_LIT, SAFE_TRAIL_LIT, LAB_TRAIL_LIT, DAMP_FLOOR_LIT, DAMP_FLOOR_DARK, DIRT_LIT, DIRT_DARK, FLOOR_ARENA_LIT, FLOOR_ARENA_DARK :: GroupName TileKind++-- ** Allure-specific+pattern HABITAT_CONTAINMENT_WALL, TRANSPORT_ROUTE, ORIELS_FENCE, AIRLOCK_FENCE, EMPTY_AIRLOCK_FENCE, OPENABLE_WALL, TRAPPABLE_WALL, OILY_FLOOR_LIT, OILY_FLOOR_DARK :: GroupName TileKind++pattern EMPTY_SET_LIT, EMPTY_SET_DARK, NOISE_SET_LIT, POWER_SET_LIT, POWER_SET_DARK, BATTLE_SET_LIT, BATTLE_SET_DARK, BRAWL_SET_LIT, SHOOTOUT_SET_LIT, ZOO_SET_LIT, ZOO_SET_DARK, ESCAPE_SET_LIT, ESCAPE_SET_DARK, AMBUSH_SET_LIT, AMBUSH_SET_DARK, ARENA_SET_LIT, ARENA_SET_DARK :: GroupName TileKind++-- ** Allure-specific+pattern ROGUE_SET, MUSEUM_SET_LIT, MUSEUM_SET_DARK, HUNT_SET_LIT, EXIT_SET_LIT :: GroupName TileKind++-- ** Used in PlaceKind, but not in CaveKind.+pattern TREE_SHADE_WALKABLE_LIT, TREE_SHADE_WALKABLE_DARK, SMOKE_CLUMP_LIT, SMOKE_CLUMP_DARK, BUSH_CLUMP_LIT, BUSH_CLUMP_DARK, FOG_CLUMP_LIT, FOG_CLUMP_DARK, STAIR_TERMINAL_LIT, STAIR_TERMINAL_DARK, SIGNBOARD, STAIRCASE_UP, ORDINARY_STAIRCASE_UP, STAIRCASE_OUTDOOR_UP, GATED_STAIRCASE_UP, STAIRCASE_DOWN, ORDINARY_STAIRCASE_DOWN, STAIRCASE_OUTDOOR_DOWN, GATED_STAIRCASE_DOWN, ESCAPE_UP, ESCAPE_DOWN, ESCAPE_OUTDOOR_DOWN :: GroupName TileKind++-- ** Allure-specific+pattern BUSH_GROVE_LIT, BUSH_GROVE_DARK, UNDERBRUSH_CLUMP_LIT, UNDERBRUSH_CLUMP_DARK, ASHES_SMOKE_LIT, ASHES_SMOKE_DARK, RECT_WINDOWS, DOORLESS_MACHINERY, PUMPS_LIT, PUMPS_DARK, DOORLESS_WALL, OIL_RESIDUE_LIT, OIL_RESIDUE_DARK, LIFT_TERMINAL_LIT, LIFT_TERMINAL_DARK, STAIRCASE_LIFT_UP, STAIRCASE_LIFT_DOWN, GATED_LIFT_UP, GATED_LIFT_DOWN, DECON_STAIRCASE_UP, DECON_STAIRCASE_DOWN, DECON_LIFT_UP, DECON_LIFT_DOWN, WELDED_STAIRCASE_UP, WELDED_LIFT_UP, ESCAPE_SPACESHIP_DOWN, ORDINARY_LIFT_UP, ORDINARY_LIFT_DOWN, RUBBLE_OR_WASTE_LIT, RUBBLE_OR_WASTE_DARK, CACHE_DEPOSIT, CACHE_JEWELRY, CACHE_MAZE, CACHE_SHUTTLE, TRAPPED_DOOR, FLOOR_ACTOR_ITEM, FLOOR_ACTOR_ITEM_LIT, STUCK_DOOR, BARREL :: GroupName TileKind++-- ** Used only internally in other TileKind definitions or never used.+pattern TREE_BURNING_OR_NOT, BUSH_BURNING_OR_NOT :: GroupName TileKind++-- ** Allure-specific+pattern OBSCURED_WALL, CACHE_DEPOSIT_OR_NOT, CACHE_DEPOSIT_BREACHED, CACHE_JEWELRY_OR_NOT, CACHE_JEWELRY_TRAPPED_OR_NOT, CACHE_ABANDONED_OR_NOT, RUBBLE_BURNING_OR_NOT :: GroupName TileKind++-- * Not used, but needed, because auto-generated. Not singletons.+pattern BRAWL_SET_DARK, NOISE_SET_DARK, SHOOTOUT_SET_DARK, EXIT_SET_DARK, HUNT_SET_DARK, FLOOR_ACTOR_ITEM_DARK :: GroupName TileKind++-- ** Used in CaveKind and perhaps elsewhere (or a dark/lit version thereof).+pattern FILLER_WALL = GroupName "fillerWall"+pattern FLOOR_CORRIDOR_LIT = GroupName "floorCorridorLit"+pattern FLOOR_CORRIDOR_DARK = GroupName "floorCorridorDark"+pattern TRAIL_LIT = GroupName "trailLit"+pattern SAFE_TRAIL_LIT = GroupName "safeTrailLit"+pattern LAB_TRAIL_LIT = GroupName "labTrailLit"+  -- these three would work without @_LIT@, but it will be needed when+  -- in the future a lit trail is made from terrain that has an autogenerated+  -- dark variant+pattern DAMP_FLOOR_LIT = GroupName "damp floor Lit"+pattern DAMP_FLOOR_DARK = GroupName "damp floor Dark"+pattern DIRT_LIT = GroupName "dirt Lit"+pattern DIRT_DARK = GroupName "dirt Dark"+pattern FLOOR_ARENA_LIT = GroupName "floorArenaLit"+pattern FLOOR_ARENA_DARK = GroupName "floorArenaDark"++-- ** Allure-specific+pattern HABITAT_CONTAINMENT_WALL = GroupName "habitat containment wall"+pattern TRANSPORT_ROUTE = GroupName "transport route"+pattern ORIELS_FENCE = GroupName "oriels fence"+pattern AIRLOCK_FENCE = GroupName "airlock fence"+pattern EMPTY_AIRLOCK_FENCE = GroupName "empty airlock fence"+pattern OPENABLE_WALL = GroupName "openableWall"+pattern TRAPPABLE_WALL = GroupName "trappableWall"+pattern OILY_FLOOR_LIT = GroupName "oily floor Lit"+pattern OILY_FLOOR_DARK = GroupName "oily floor Dark"++-- ** Used in CaveKind and perhaps elsewhere; sets of tiles for filling cave.+pattern EMPTY_SET_LIT = GroupName "emptySetLit"+pattern EMPTY_SET_DARK = GroupName "emptySetDark"+pattern NOISE_SET_LIT = GroupName "noiseSetLit"+pattern POWER_SET_LIT = GroupName "powerSetLit"+pattern POWER_SET_DARK = GroupName "powerSetDark"+pattern BATTLE_SET_LIT = GroupName "battleSetLit"+pattern BATTLE_SET_DARK = GroupName "battleSetDark"+pattern BRAWL_SET_LIT = GroupName "brawlSetLit"+pattern SHOOTOUT_SET_LIT = GroupName "shootoutSetLit"+pattern ZOO_SET_LIT = GroupName "zooSetLit"+pattern ZOO_SET_DARK = GroupName "zooSetDark"+pattern ESCAPE_SET_LIT = GroupName "escapeSetLit"+pattern ESCAPE_SET_DARK = GroupName "escapeSetDark"+pattern AMBUSH_SET_LIT = GroupName "ambushSetLit"+pattern AMBUSH_SET_DARK = GroupName "ambushSetDark"+pattern ARENA_SET_LIT = GroupName "arenaSetLit"+pattern ARENA_SET_DARK = GroupName "arenaSetDark"++-- ** Allure-specific+pattern ROGUE_SET = GroupName "rogueSet"+pattern MUSEUM_SET_LIT = GroupName "museumSetLit"+pattern MUSEUM_SET_DARK = GroupName "museumSetDark"+pattern HUNT_SET_LIT = GroupName "huntSetLit"+pattern EXIT_SET_LIT = GroupName "exitSetLit"++-- ** Used in PlaceKind, but not in CaveKind. Not singletons.+pattern TREE_SHADE_WALKABLE_LIT = GroupName "treeShadeWalkableLit"+pattern TREE_SHADE_WALKABLE_DARK = GroupName "treeShadeWalkableDark"+pattern SMOKE_CLUMP_LIT = GroupName "smokeClumpLit"+pattern SMOKE_CLUMP_DARK = GroupName "smokeClumpDark"+pattern BUSH_CLUMP_LIT = GroupName "bushClumpLit"+pattern BUSH_CLUMP_DARK = GroupName "bushClumpDark"+pattern FOG_CLUMP_LIT = GroupName "fogClumpLit"+pattern FOG_CLUMP_DARK = GroupName "fogClumpDark"+pattern STAIR_TERMINAL_LIT = GroupName "stair terminal Lit"+pattern STAIR_TERMINAL_DARK = GroupName "stair terminal Dark"+pattern SIGNBOARD = GroupName "signboard"+pattern STAIRCASE_UP = GroupName "staircase up"+pattern ORDINARY_STAIRCASE_UP = GroupName "ordinary staircase up"+pattern STAIRCASE_OUTDOOR_UP = GroupName "staircase outdoor up"+pattern GATED_STAIRCASE_UP = GroupName "gated staircase up"+pattern STAIRCASE_DOWN = GroupName "staircase down"+pattern ORDINARY_STAIRCASE_DOWN = GroupName "ordinary staircase down"+pattern STAIRCASE_OUTDOOR_DOWN = GroupName "staircase outdoor down"+pattern GATED_STAIRCASE_DOWN = GroupName "gated staircase down"+pattern ESCAPE_UP = GroupName "escape up"+pattern ESCAPE_DOWN = GroupName "escape down"+pattern ESCAPE_OUTDOOR_DOWN = GroupName "escape outdoor down"++-- ** Used in PlaceKind, but not in CaveKind. Singletons.+pattern S_LAMP_POST = GroupName "lamp post"+pattern S_TREE_LIT = GroupName "tree Lit"+pattern S_TREE_DARK = GroupName "tree Dark"+pattern S_PULPIT = GroupName "pulpit"+pattern S_BUSH_LIT = GroupName "bush Lit"+pattern S_FOG_LIT = GroupName "fog Lit"+pattern S_SMOKE_LIT = GroupName "smoke Lit"+pattern S_FLOOR_ACTOR_LIT = GroupName "floor with actors Lit"+pattern S_FLOOR_ACTOR_DARK = GroupName "floor with actors Dark"+pattern S_FLOOR_ASHES_LIT = GroupName "floor with ashes Lit"+pattern S_FLOOR_ASHES_DARK = GroupName "floor with ashes Dark"+pattern S_SHADED_GROUND = GroupName "shaded ground"++-- ** Allure-specific+pattern BUSH_GROVE_LIT = GroupName "bushGroveLit"+pattern BUSH_GROVE_DARK = GroupName "bushGroveDark"+pattern UNDERBRUSH_CLUMP_LIT = GroupName "underbrushClumpLit"+pattern UNDERBRUSH_CLUMP_DARK = GroupName "underbrushClumpDark"+pattern ASHES_SMOKE_LIT = GroupName "ashesSmokeLit"+pattern ASHES_SMOKE_DARK = GroupName "ashesSmokeDark"+pattern RECT_WINDOWS = GroupName "rectWindows"+pattern DOORLESS_MACHINERY = GroupName "doorlessMachinery"+pattern PUMPS_LIT = GroupName "pumpsLit"+pattern PUMPS_DARK = GroupName "pumpsDark"+pattern DOORLESS_WALL = GroupName "doorlessWall"+pattern OIL_RESIDUE_LIT = GroupName "oilResidueLit"+pattern OIL_RESIDUE_DARK = GroupName "oilResidueDark"+pattern LIFT_TERMINAL_LIT = GroupName "lift terminal Lit"+pattern LIFT_TERMINAL_DARK = GroupName "lift terminal Dark"+pattern STAIRCASE_LIFT_UP = GroupName "staircase lift up"+pattern STAIRCASE_LIFT_DOWN = GroupName "staircase lift down"+pattern GATED_LIFT_UP = GroupName "gated lift up"+pattern GATED_LIFT_DOWN = GroupName "gated lift down"+pattern DECON_STAIRCASE_UP = GroupName "decon staircase up"+pattern DECON_STAIRCASE_DOWN = GroupName "decon staircase down"+pattern DECON_LIFT_UP = GroupName "decon lift up"+pattern DECON_LIFT_DOWN = GroupName "decon lift down"+pattern WELDED_STAIRCASE_UP = GroupName "welded staircase up"+pattern WELDED_LIFT_UP = GroupName "welded lift up"+pattern ESCAPE_SPACESHIP_DOWN = GroupName "escape spaceship down"+pattern ORDINARY_LIFT_UP = GroupName "ordinary lift up"+pattern ORDINARY_LIFT_DOWN = GroupName "ordinary lift down"+pattern RUBBLE_OR_WASTE_LIT = GroupName "rubbleOrWaste_Lit"+pattern RUBBLE_OR_WASTE_DARK = GroupName "rubbleOrWaste_Dark"+pattern CACHE_DEPOSIT = GroupName "cache deposit"+pattern CACHE_JEWELRY = GroupName "cache jewelry"+pattern CACHE_MAZE = GroupName "cache maze"+pattern CACHE_SHUTTLE = GroupName "cache shuttle"+pattern TRAPPED_DOOR = GroupName "trapped door"+pattern FLOOR_ACTOR_ITEM = GroupName "floorActorItem"+pattern FLOOR_ACTOR_ITEM_LIT = GroupName "floorActorItemLit"+pattern STUCK_DOOR = GroupName "stuck door"+pattern BARREL = GroupName "barrel"++pattern S_POOL_LIT = GroupName "poolLit"+pattern S_POOL_DARK = GroupName "poolDark"+pattern S_OIL_SPILL = GroupName "oil spill"+pattern S_FROZEN_PATH = GroupName "frozen path"+pattern S_LIFT_SHAFT = GroupName "lift shaft"+pattern S_REINFORCED_WALL = GroupName "reinforced wall"+pattern S_SHUTTLE_HULL = GroupName "shuttle hull"+pattern S_HARDWARE_RACK = GroupName "hardware rack"++-- ** Used only internally in other TileKind definitions. Not singletons.+pattern TREE_BURNING_OR_NOT = GroupName "tree burning or not"+pattern BUSH_BURNING_OR_NOT = GroupName "bush burning or not"++-- ** Used only internally in other TileKind definitions. Singletons.++pattern S_RUBBLE_PILE = GroupName "rubble pile"+pattern S_SHALLOW_WATER_LIT = GroupName "shallow water Lit"+pattern S_SIGNBOARD_UNREAD = GroupName "signboard unread"++-- ** Allure-specific+pattern OBSCURED_WALL = GroupName "obscured wall"+pattern CACHE_DEPOSIT_OR_NOT = GroupName "cache deposit or not"+pattern CACHE_DEPOSIT_BREACHED = GroupName "cache deposit breached"+pattern CACHE_JEWELRY_OR_NOT = GroupName "cache jewelry or not"+pattern CACHE_JEWELRY_TRAPPED_OR_NOT = GroupName "cache jewelry trapped or not"+pattern CACHE_ABANDONED_OR_NOT = GroupName "cache abandoned or not"+pattern RUBBLE_BURNING_OR_NOT = GroupName "rubble burning or not"++pattern S_SUSPECT_WALL = GroupName "suspect wall"+pattern S_CLOSED_DOOR = GroupName "closed door"+pattern S_OPEN_DOOR = GroupName "open door"+pattern S_STAIRCASE_TRAP_DOWN_OIL = GroupName "slippery staircase down"+pattern S_BURNING_INSTALLATION = GroupName "burning installation"+pattern S_BURNING_TREE = GroupName "burning tree"+pattern S_BURNING_BUSH = GroupName "burning bush"+pattern S_BURNING_UNDERBRUSH = GroupName "burning underbrush"+pattern S_BURNING_OIL = GroupName "burning oil"+pattern S_UNDERBRUSH_LIT = GroupName "underbrush Lit"+pattern S_UNDERBRUSH_DARK = GroupName "underbrush Dark"++-- * Not used, but needed, because auto-generated. Not singletons.+-- This is a rotten compromise, because these are synthesized below,+-- so typos can happen. Similarly below+pattern BRAWL_SET_DARK = GroupName "brawlSetDark"+pattern NOISE_SET_DARK = GroupName "noiseSetDark"+pattern SHOOTOUT_SET_DARK = GroupName "shootoutSetDark"+pattern EXIT_SET_DARK = GroupName "exitSetDark"+pattern HUNT_SET_DARK = GroupName "huntSetDark"+pattern FLOOR_ACTOR_ITEM_DARK = GroupName "floorActorItemDark"++-- * Not used, but needed, because auto-generated. Singletons.+pattern S_BUSH_DARK = GroupName "bush Dark"+pattern S_SHALLOW_WATER_DARK = GroupName "shallow water Dark"++-- * Content++content :: [TileKind]+content =+  [unknown, unknownOuterFence, basicOuterFence, bedrock, wall, wallSuspect, wallObscured, wallObscuredDefaced, wallObscuredFrescoed, pillar, pillarCache, lampPost, signboardUnread, signboardRead, treeLit, treeBurnt, treeBurning, rubble, rubbleSpice, doorTrapped, doorClosed, stairsUp, stairsTrappedUp, stairsOutdoorUp, stairsGatedUp, stairsDown, stairsTrappedDown, stairsOutdoorDown, stairsGatedDown, escapeUp, escapeDown, escapeOutdoorDown, wallGlass, wallGlassSpice, pillarIce, pulpit, bushLit, bushBurnt, bushBurning, fog, fogDark, smoke, smokeDark, doorOpen, floorCorridor, floorArena, floorDamp, floorDirt, floorDirtSpice, floorActor, floorActorItem, floorAshes, shallowWater, shallowWaterSpice, shallowWater2, floorRed, floorBlue, floorBrown, floorArenaShade ]+  ++ map makeDarkColor ldarkColorable+  -- Allure-specific+  ++ [oriel, outerHullWall, rubbleBurning, rubbleBurningSpice, wallOpenable, wallObscuredSafety, signboardReadExtinguisher, wallObscured3dBillboard, wallObscuredPipework, wallObscuredScary, liftShaft, rock, pillarCache2, pillarCache3, pillarCache4, pillarCache5, stairsTrappedDownOil, stairsDecontaminatingUp, stairsWelded, stairsLiftUp, stairsLiftTrappedUp, stairsLiftGatedUp, stairsLiftDecontaminatingUp, stairsLiftWelded, stairsDecontaminatingDown, stairsLiftDown, stairsLiftTrappedDown, stairsLiftGatedDown, stairsLiftDecontaminatingDown, escapeSpaceshipDown, emptyAirlock, reinforcedWall, reinforcedWallSpice, wallShuttle, wallShuttleSpice, doorStuck, barrel, barrelSpice, machineWall, machineWallSpice, bushEdible, bushEdibleSpice, underbrushBurning, floorOily, oilSpill, oilSpillSpice, oilBurning, floorWindow, underbrush, workshop]++unknown,    unknownOuterFence, basicOuterFence, bedrock, wall, wallSuspect, wallObscured, wallObscuredDefaced, wallObscuredFrescoed, pillar, pillarCache, lampPost, signboardUnread, signboardRead, treeLit, treeBurnt, treeBurning, rubble, rubbleSpice, doorTrapped, doorClosed, stairsUp, stairsTrappedUp, stairsOutdoorUp, stairsGatedUp, stairsDown, stairsTrappedDown, stairsOutdoorDown, stairsGatedDown, escapeUp, escapeDown, escapeOutdoorDown, wallGlass, wallGlassSpice, pillarIce, pulpit, bushLit, bushBurnt, bushBurning, fog, fogDark, smoke, smokeDark, doorOpen, floorCorridor, floorArena, floorDamp, floorDirt, floorDirtSpice, floorActor, floorActorItem, floorAshes, shallowWater, shallowWaterSpice, shallowWater2, floorRed, floorBlue, floorBrown, floorArenaShade :: TileKind+-- Allure-specific+oriel,       outerHullWall, rubbleBurning, rubbleBurningSpice, wallOpenable, wallObscuredSafety, signboardReadExtinguisher, wallObscured3dBillboard, wallObscuredPipework, wallObscuredScary, liftShaft, rock, pillarCache2, pillarCache3, pillarCache4, pillarCache5, stairsTrappedDownOil, stairsDecontaminatingUp, stairsWelded, stairsLiftUp, stairsLiftTrappedUp, stairsLiftGatedUp, stairsLiftDecontaminatingUp, stairsLiftWelded, stairsDecontaminatingDown, stairsLiftDown, stairsLiftTrappedDown, stairsLiftGatedDown, stairsLiftDecontaminatingDown, escapeSpaceshipDown, emptyAirlock, reinforcedWall, reinforcedWallSpice, wallShuttle, wallShuttleSpice, doorStuck, barrel, barrelSpice, machineWall, machineWallSpice, bushEdible, bushEdibleSpice, underbrushBurning, floorOily, oilSpill, oilSpillSpice, oilBurning, floorWindow, underbrush, workshop :: TileKind++ldarkColorable :: [TileKind]+ldarkColorable = [treeLit, bushLit, floorCorridor, floorArena, floorDamp, floorDirt, floorDirtSpice, floorActor, floorActorItem, shallowWater, shallowWaterSpice, shallowWater2, floorOily]++-- Symbols to be used:+--         LOS    noLOS+-- Walk    .'~    :;+-- noWalk  %^     #O&<>++--+-- can be opened ^&++-- can be closed '+-- some noWalk can be changed without opening, regardless of symbol+-- not used yet:+-- : (curtain, etc., not flowing, but solid and static)+-- ` (not distinct enough from ' and already used for some blasts)++-- White, cyan and green terrain is usually inert, red is burning or trapped,+-- blue activable or trapped, magenta searchable or activable.++-- Note that for AI hints and UI comfort, most multiple-use @Embed@ tiles+-- should have a variant, which after first use transforms into a different+-- colour tile without @ChangeTo@ and similar (which then AI no longer touches).+-- If a tile is supposed to be repeatedly activated by AI (e.g., cache),+-- it should keep @ChangeTo@ for the whole time.++-- * Main tiles, modified for Allure; some removed++-- ** Not walkable++-- *** Not clear++unknown = TileKind  -- needs to have index 0 and alter 1; no other with 1+  { tsymbol  = ' '+  , tname    = "unknown space"+  , tfreq    = [(S_UNKNOWN_SPACE, 1)]+  , tcolor   = defFG+  , tcolor2  = defFG+  , talter   = 1+  , tfeature = [Dark]+  }+unknownOuterFence = TileKind+  { tsymbol  = ' '+  , tname    = "unknown space"+  , tfreq    = [(S_UNKNOWN_OUTER_FENCE, 1)]+  , tcolor   = defFG+  , tcolor2  = defFG+  , talter   = maxBound  -- impenetrable+  , tfeature = [Dark]+  }+basicOuterFence = TileKind+  { tsymbol  = '#'+  , tname    = "habitat containment wall"+  , tfreq    = [(HABITAT_CONTAINMENT_WALL, 1)]+  , tcolor   = BrBlack+  , tcolor2  = BrBlack+  , talter   = maxBound  -- impenetrable+  , tfeature = []+  }+bedrock = TileKind+  { tsymbol  = '#'+  , tname    = "wall"+  , tfreq    = [ (FILLER_WALL, 1), (LEGEND_LIT, 100), (LEGEND_DARK, 100)+               , (ROGUE_SET, 60), (MUSEUM_SET_DARK, 4), (NOISE_SET_LIT, 450)+               , (POWER_SET_DARK, 450), (BATTLE_SET_DARK, 250)+               , (ESCAPE_SET_DARK, 4)+               , (STAIR_TERMINAL_LIT, 100), (STAIR_TERMINAL_DARK, 100)+               , (DOORLESS_WALL, 80), (DOORLESS_MACHINERY, 1) ]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 100+  , tfeature = []+  }+wall = bedrock  -- fireproof+  { tfreq    = [(TRAPPABLE_WALL, 1), (RECT_WINDOWS, 80)]+  , tfeature = [BuildAs S_SUSPECT_WALL]+  }+wallSuspect = TileKind  -- only on client+  { tsymbol  = '#'+  , tname    = "suspect wall"+  , tfreq    = [(S_SUSPECT_WALL, 1)]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 2+  , tfeature = [ RevealAs TRAPPED_DOOR+               , ObscureAs OBSCURED_WALL+               ]+  }+wallObscured = TileKind+  { tsymbol  = '#'+  , tname    = "scratched wall"+  , tfreq    = [(OBSCURED_WALL, 25)]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 5+  , tfeature = [ Embed SCRATCH_ON_WALL+               , HideAs S_SUSPECT_WALL+               ]+  }+wallObscuredDefaced = TileKind+  { tsymbol  = '#'+  , tname    = "defaced wall"+  , tfreq    = [ (OBSCURED_WALL, 25), (ESCAPE_SET_DARK, 2)+               , (MUSEUM_SET_DARK, 2) ]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 5+  , tfeature = [ Embed OBSCENE_PICTOGRAM+               , HideAs S_SUSPECT_WALL+               ]+  }+wallObscuredFrescoed = TileKind+  { tsymbol  = '#'+  , tname    = "subtle mural"+  , tfreq    = [(OBSCURED_WALL, 5), (MUSEUM_SET_DARK, 2)]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 5+  , tfeature = [ Embed SUBTLE_FRESCO+               , HideAs S_SUSPECT_WALL+               ]  -- a bit beneficial, but AI would loop if allowed to trigger+                  -- so no @ConsideredByAI@+  }+pillar = TileKind+  { tsymbol  = '0'+  , tname    = "construction beam"+  , tfreq    = [ (LEGEND_LIT, 100), (LEGEND_DARK, 100)+               , (MUSEUM_SET_DARK, 20), (EMPTY_SET_LIT, 60) ]+  , tcolor   = BrCyan  -- not BrWhite, to tell from heroes+  , tcolor2  = Cyan+  , talter   = 100+  , tfeature = []+  }+pillarCache = TileKind+  { tsymbol  = '#'+  , tname    = "abandoned stash"+  , tfreq    = [ (CACHE_ABANDONED_OR_NOT, 40)+               , (CACHE_MAZE, 33), (CACHE_SHUTTLE, 25) ]+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , talter   = 5+  , tfeature = [ Embed ABANDONED_CACHE+               , ChangeTo CACHE_ABANDONED_OR_NOT, ConsideredByAI ]+      -- Not explorable, but prominently placed, so hard to miss.+      -- Very beneficial, so AI eager to trigger.+  }+lampPost = TileKind+  { tsymbol  = '0'+  , tname    = "lamp post"+  , tfreq    = [(S_LAMP_POST, 1)]+  , tcolor   = BrYellow+  , tcolor2  = Brown+  , talter   = 100+  , tfeature = []  -- embed something and explain how there's often+                   -- artificial ambient light in the habitats, but not in all+                   -- of them and in both cases lamps are used to provide fancy+                   -- (extra) lighting; say how low energy drain, such as+                   -- permanent ambient light, is not a problem due to tech+                   -- and also because it's a tiny fraction of what is needed+                   -- for the ecosystem/life support+  }+signboardUnread = TileKind  -- client only, indicates never used by this faction+  { tsymbol  = '0'+  , tname    = "signboard"+  , tfreq    = [(S_SIGNBOARD_UNREAD, 1)]+  , tcolor   = BrCyan+  , tcolor2  = Cyan+  , talter   = 5+  , tfeature = [ ConsideredByAI  -- changes after use, so safe for AI, which+                                 -- in this way uses all kinds of signboards+               , RevealAs SIGNBOARD  -- to display as hidden+               ]+  }+signboardRead = TileKind+  { tsymbol  = '0'+  , tname    = "signboard"+  , tfreq    = [ (SIGNBOARD, 80), (EMPTY_SET_LIT, 7)+               , (ARENA_SET_LIT, 1), (ARENA_SET_DARK, 2), (MUSEUM_SET_DARK, 1)+               , (ESCAPE_SET_DARK, 2) ]+  , tcolor   = BrCyan+  , tcolor2  = Cyan+  , talter   = 5+  , tfeature = [Embed SIGNAGE, HideAs S_SIGNBOARD_UNREAD]+                 -- can't transform or the hidden version would not trigger+                 -- but only reveal the real version; needed extra keystroke+  }+treeLit = TileKind+  { tsymbol  = '0'+  , tname    = "tree"+  , tfreq    = [ (ARENA_SET_LIT, 9), (EMPTY_SET_LIT, 4), (BRAWL_SET_LIT, 140)+               , (SHOOTOUT_SET_LIT, 10), (HUNT_SET_LIT, 10)+               , (ESCAPE_SET_LIT, 35), (ZOO_SET_DARK, 20)+               , (S_TREE_LIT, 1) ]+  , tcolor   = BrGreen+  , tcolor2  = Green+  , talter   = 4+  , tfeature = [ChangeWith ProjYes [(1, FIRE_SOURCE)] S_BURNING_TREE]+  }+treeBurnt = TileKind+  { tsymbol  = '0'+  , tname    = "burnt tree"+  , tfreq    = [(ZOO_SET_DARK, 10), (TREE_BURNING_OR_NOT, 30)]+  , tcolor   = BrBlack+  , tcolor2  = BrBlack+  , talter   = 4+  , tfeature = [Dark]  -- even burned too hard to topple+  }+treeBurning = TileKind  -- present in EMPTY_SET_LIT as early light/fire source+  { tsymbol  = '0'+  , tname    = "burning tree"+  , tfreq    = [ (EMPTY_SET_LIT, 2), (ZOO_SET_DARK, 60)+               , (TREE_BURNING_OR_NOT, 70), (S_BURNING_TREE, 1) ]+  , tcolor   = BrRed+  , tcolor2  = Red+  , talter   = 5+  , tfeature = [Embed BIG_FIRE, ChangeTo TREE_BURNING_OR_NOT]+      -- too tall to douse with a fireproof cloth or water; have to break off+      -- and isolate smaller branches and let it smolder out+      -- TODO: breaking the burning tree has more use when it periodically+      -- explodes, hitting and lighting up the team and so betraying it+  }+rubble = TileKind+  { tsymbol  = '&'+  , tname    = "rubble pile"+  , tfreq    = [ (S_RUBBLE_PILE, 1), (RUBBLE_BURNING_OR_NOT, 50)+               , (LEGEND_LIT, 1), (LEGEND_DARK, 1)+               , (STAIR_TERMINAL_LIT, 6), (STAIR_TERMINAL_DARK, 6)+               , (LIFT_TERMINAL_LIT, 6), (LIFT_TERMINAL_DARK, 6)+               , (EMPTY_SET_LIT, 12), (EXIT_SET_LIT, 6)+               , (NOISE_SET_LIT, 40), (POWER_SET_DARK, 120)+               , (ZOO_SET_DARK, 150), (AMBUSH_SET_DARK, 3) ]+  , tcolor   = BrYellow+  , tcolor2  = Brown+  , talter   = 4  -- boss can dig through+  , tfeature = [ OpenWith ProjYes [(1, BLAST_SOURCE)] S_FLOOR_ASHES_LIT+                   -- can as well be first, because projectiles can't activate+                   -- embeds with non-zero talter; for non-projectiles,+                   -- if a @BLAST_SOURCE@ item could be found,+                   -- this is a safe way to open rubble, with no loot though+               , Embed RUBBLE+               , OpenTo S_FLOOR_ASHES_LIT ]+      -- Getting the item is risky and, e.g., AI doesn't attempt it.+      -- Also, AI doesn't go out of its way to clear the way for heroes.+      -- Rubble can't be ignited, but burning installation, when doused,+      -- becomes rubble. That's different than with trees and bushes.+  }+rubbleSpice = rubble+  { tfreq    = [ (SMOKE_CLUMP_LIT, 1), (SMOKE_CLUMP_DARK, 1)+               , (RUBBLE_OR_WASTE_LIT, 1), (RUBBLE_OR_WASTE_DARK, 1)+               , (CACHE_DEPOSIT, 33) ]+  , tfeature = Spice : tfeature rubble+  }+doorTrapped = TileKind+  { tsymbol  = '+'+  , tname    = "trapped door"+  , tfreq    = [(TRAPPED_DOOR, 1)]+  , tcolor   = BrRed+  , tcolor2  = Red+  , talter   = 2+  , tfeature = [ ChangeWith ProjNo [(1, WIRECUTTING_TOOL)] S_CLOSED_DOOR+               , Embed DOORWAY_TRAP+               , OpenTo S_OPEN_DOOR+               , HideAs S_SUSPECT_WALL+               ]+  }+doorClosed = TileKind  -- fireproof+  { tsymbol  = '+'+  , tname    = "closed door"+  , tfreq    = [(LEGEND_LIT, 100), (LEGEND_DARK, 100), (S_CLOSED_DOOR, 1)]+  , tcolor   = Brown+  , tcolor2  = BrBlack+  , talter   = 2+  , tfeature = [ OpenTo S_OPEN_DOOR  -- never hidden+               , OpenWith ProjYes [(1, BLAST_SOURCE)] S_OPEN_DOOR ]+  }+stairsUp = TileKind  -- fireproof+  { tsymbol  = '<'+  , tname    = "staircase up"+  , tfreq    = [(STAIRCASE_UP, 90), (ORDINARY_STAIRCASE_UP, 1)]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 0  -- very easy stairs, unlike all others; projectiles trigger+  , tfeature = [Embed STAIRS_UP, ConsideredByAI]+  }+stairsTrappedUp = TileKind+  { tsymbol  = '<'+  , tname    = "windy staircase up"+  , tfreq    = [(STAIRCASE_UP, 10)]+  , tcolor   = BrRed+  , tcolor2  = Red+  , talter   = talterForStairs+  , tfeature = [ Embed STAIRS_UP+               , Embed STAIRS_TRAP_UP+               , ConsideredByAI, ChangeTo ORDINARY_STAIRCASE_UP ]+                 -- AI uses despite the trap; exploration more important+  }+stairsOutdoorUp = stairsUp+  { tfreq    = [(STAIRCASE_OUTDOOR_UP, 1)]+  , talter   = talterForStairs+  , tfeature = [Embed STAIRS_UP_OUTDOOR, ConsideredByAI]+  }+stairsGatedUp = stairsUp+  { tname    = "gated staircase up"+  , tfreq    = [(GATED_STAIRCASE_UP, 1)]+  , talter   = talterForStairs + 2  -- animals and bosses can't use+  }+stairsDown = TileKind+  { tsymbol  = '>'+  , tname    = "staircase down"+  , tfreq    = [(STAIRCASE_DOWN, 90), (ORDINARY_STAIRCASE_DOWN, 1)]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 0  -- very easy stairs, unlike all others; projectiles trigger+  , tfeature = [ ChangeWith ProjYes [(1, OIL_SOURCE)] S_STAIRCASE_TRAP_DOWN_OIL+                   -- even random oil explosions can create this trap+               , Embed STAIRS_DOWN+               , ConsideredByAI ]+  }+stairsTrappedDown = TileKind+  { tsymbol  = '>'+  , tname    = "cracked staircase down"+  , tfreq    = [(STAIRCASE_DOWN, 5)]+  , tcolor   = BrRed+  , tcolor2  = Red+  , talter   = talterForStairs+  , tfeature = [ Embed STAIRS_DOWN+               , Embed STAIRS_TRAP_DOWN+               , ConsideredByAI, ChangeTo ORDINARY_STAIRCASE_DOWN ]+  }+stairsOutdoorDown = stairsDown+  { tfreq    = [(STAIRCASE_OUTDOOR_DOWN, 1)]+  , talter   = talterForStairs+  , tfeature = [Embed STAIRS_DOWN_OUTDOOR, ConsideredByAI]+  }+stairsGatedDown = stairsDown+  { tname    = "gated staircase down"+  , tfreq    = [(GATED_STAIRCASE_DOWN, 1)]+  , talter   = talterForStairs + 2  -- animals and bosses can't use+  }+escapeUp = TileKind+  { tsymbol  = '<'+  , tname    = "exit hatch up"+  , tfreq    = [(LEGEND_LIT, 1), (LEGEND_DARK, 1), (ESCAPE_UP, 1)]+  , tcolor   = BrYellow+  , tcolor2  = BrYellow+  , talter   = 0  -- anybody can escape (or guard escape)+  , tfeature = [Embed ESCAPE, ConsideredByAI]+  }+escapeDown = TileKind+  { tsymbol  = '>'+  , tname    = "exit trapdoor down"+  , tfreq    = [(LEGEND_LIT, 1), (LEGEND_DARK, 1), (ESCAPE_DOWN, 1)]+  , tcolor   = BrYellow+  , tcolor2  = BrYellow+  , talter   = 0  -- anybody can escape (or guard escape)+  , tfeature = [Embed ESCAPE, ConsideredByAI]+  }+escapeOutdoorDown = escapeDown+  { tname    = "exit back to town"+  , tfreq    = [(ESCAPE_OUTDOOR_DOWN, 1)]+  }++-- *** Clear++wallGlass = TileKind+  { tsymbol  = '%'+  , tname    = "transparent polymer wall"+  , tfreq    = [(LEGEND_LIT, 1), (LEGEND_DARK, 1), (MUSEUM_SET_DARK, 8)]+  , tcolor   = BrCyan+  , tcolor2  = Cyan+  , talter   = 10+  , tfeature = [ BuildAs S_CLOSED_DOOR  -- when ending a corridor, have doors+               , Clear ]+  }+wallGlassSpice = wallGlass+  { tfreq    = [ (RECT_WINDOWS, 20)+               , (CACHE_JEWELRY, 40)+               , (CACHE_JEWELRY_OR_NOT, 60)+               , (CACHE_JEWELRY_TRAPPED_OR_NOT, 60) ]+  , tfeature = Spice : tfeature wallGlass+  }+pillarIce = TileKind+  { tsymbol  = '^'+  , tname    = "ice buildup"+  , tfreq    = [ (LEGEND_LIT, 1), (LEGEND_DARK, 1), (NOISE_SET_LIT, 200)+               , (BRAWL_SET_LIT, 15), (LIFT_TERMINAL_DARK, 4) ]+                 -- ice only in dark staircases+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , talter   = 4  -- boss can dig through+  , tfeature = [ Clear+               , Embed FROST+               , OpenTo S_SHALLOW_WATER_LIT+               , OpenWith ProjYes [(1, BLAST_SOURCE)] DAMP_FLOOR_LIT ]+  }+pulpit = TileKind+  { tsymbol  = '%'+  , tname    = "VR booth"+  , tfreq    = [(S_PULPIT, 1)]+  , tcolor   = BrYellow+  , tcolor2  = Brown+  , talter   = 5+  , tfeature = [ ChangeWith ProjYes [(1, FIRE_SOURCE)] S_BURNING_INSTALLATION+                   -- wastes the loot; cruel, but rare; usually player's fault+               , Embed LECTERN+               , Clear ]+                 -- mixed blessing, so AI ignores, saved for player's fun+  }+bushLit = TileKind+  { tsymbol  = '%'+  , tname    = "bush"+  , tfreq    = [ (S_BUSH_LIT, 1), (EMPTY_SET_LIT, 8), (ARENA_SET_LIT, 13)+               , (BRAWL_SET_LIT, 5), (SHOOTOUT_SET_LIT, 30), (HUNT_SET_LIT, 30)+               , (ESCAPE_SET_LIT, 40), (ZOO_SET_DARK, 100)+               , (BUSH_CLUMP_LIT, 2), (BUSH_CLUMP_DARK, 2)  -- always lit+               , (BUSH_GROVE_LIT, 5), (BUSH_GROVE_DARK, 5)  -- always lit+               , (PUMPS_LIT, 300)  -- dark in PUMPS_DARK+               , (LIFT_TERMINAL_LIT, 4) ]+  , tcolor   = BrGreen+  , tcolor2  = Green+  , talter   = 4+  , tfeature = [ ChangeWith ProjYes [(1, FIRE_SOURCE)] S_BURNING_BUSH+               , Clear ]+                 -- too tough to topple, has to be burned first+  }+bushBurnt = TileKind+  { tsymbol  = '%'+  , tname    = "burnt bush"+  , tfreq    = [ (BATTLE_SET_DARK, 30), (AMBUSH_SET_DARK, 3)+               , (ZOO_SET_DARK, 100), (BUSH_BURNING_OR_NOT, 50) ]+  , tcolor   = BrBlack+  , tcolor2  = BrBlack+  , talter   = 4+  , tfeature = [Dark, Clear, OpenTo DIRT_DARK]+  }+bushBurning = TileKind+  { tsymbol  = '%'+  , tname    = "burning bush"+  , tfreq    = [ (EMPTY_SET_LIT, 3), (AMBUSH_SET_DARK, 10), (ZOO_SET_DARK, 300)+               , (BUSH_BURNING_OR_NOT, 50), (S_BURNING_BUSH, 1) ]+  , tcolor   = BrRed+  , tcolor2  = Red+  , talter   = 5+  , tfeature = [ Clear+               , Embed SMALL_FIRE_5+                   -- crafting via embed first, transformation a fallback+               , OpenWith ProjYes [(3, WATER_SOURCE)] S_SMOKE_LIT+               , ChangeWith ProjNo [(1, FIREPROOF_CLOTH)] S_BUSH_LIT+                   -- full effects experienced, but bush saved for repeat+               , ChangeTo BUSH_BURNING_OR_NOT ]+  }++-- ** Walkable++-- *** Not clear++fog = TileKind  -- always lit+  { tsymbol  = ';'+  , tname    = "faint fog"+  , tfreq    = [ (S_FOG_LIT, 1), (EMPTY_SET_LIT, 200), (NOISE_SET_LIT, 120)+               , (SHOOTOUT_SET_LIT, 30), (HUNT_SET_LIT, 30)+               , (FOG_CLUMP_LIT, 60), (FOG_CLUMP_DARK, 60)+               , (LIFT_TERMINAL_LIT, 20) ]+      -- lit fog is OK for shootout, because LOS is mutual, as opposed+      -- to dark fog, and so camper has little advantage, especially+      -- on big maps, where he doesn't know on which side of fog patch to hide+  , tcolor   = BrCyan+  , tcolor2  = Cyan+  , talter   = 0+  , tfeature = [Walkable, NoItem, OftenActor]+  }+fogDark = fog  -- always dark+  { tname    = "thick fog"+  , tfreq    = [(ESCAPE_SET_DARK, 50), (LIFT_TERMINAL_DARK, 40)]+  , tfeature = Dark : tfeature fog+  }+smoke = TileKind  -- always lit+  { tsymbol  = ';'+  , tname    = "billowing smoke"+  , tfreq    = [ (S_SMOKE_LIT, 1), (LAB_TRAIL_LIT, 1)+               , (STAIR_TERMINAL_LIT, 2), (LIFT_TERMINAL_LIT, 6)+               , (SMOKE_CLUMP_LIT, 3), (SMOKE_CLUMP_DARK, 3)+               , (ASHES_SMOKE_LIT, 1), (ASHES_SMOKE_DARK, 1)+               , (EXIT_SET_LIT, 20), (AMBUSH_SET_DARK, 20) ]+  , tcolor   = Brown+  , tcolor2  = BrBlack+  , talter   = 0+  , tfeature = [Walkable, NoItem]  -- not dark, embers+  }+smokeDark = smoke  -- always dark+  { tname    = "lingering smoke"+  , tfreq    = [ (POWER_SET_DARK, 100)+               , (ZOO_SET_DARK, 20), (AMBUSH_SET_DARK, 40), (BATTLE_SET_DARK, 5)+               , (STAIR_TERMINAL_DARK, 2), (LIFT_TERMINAL_DARK, 6) ]+  , tfeature = Dark : tfeature smoke+  }++-- *** Clear++doorOpen = TileKind  -- fireproof+  { tsymbol  = '\''+  , tname    = "open door"+  , tfreq    = [(LEGEND_LIT, 100), (LEGEND_DARK, 100), (S_OPEN_DOOR, 1)]+  , tcolor   = Brown+  , tcolor2  = BrBlack+  , talter   = 4+  , tfeature = [ Walkable, Clear, NoItem, NoActor+               , CloseTo S_CLOSED_DOOR  -- not explorable due to that+               ]+  }+floorCorridor = TileKind+  { tsymbol  = floorSymbol+  , tname    = "floor"+  , tfreq    = [(FLOOR_CORRIDOR_LIT, 1)]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 0+  , tfeature = [Walkable, Clear]  -- porous, so spilling doesn't transform+  }+floorArena = floorCorridor+  { tfreq    = [ (FLOOR_ARENA_LIT, 1), (ARENA_SET_LIT, 400)+               , (MUSEUM_SET_LIT, 400), (NOISE_SET_LIT, 50), (POWER_SET_LIT, 50)+               , (EMPTY_SET_LIT, 400), (EXIT_SET_LIT, 100), (ZOO_SET_LIT, 500) ]+  }+floorDamp = floorArena+  { tname    = "damp floor"+  , tfreq    = [ (NOISE_SET_LIT, 550), (EMPTY_SET_LIT, 3000)+               , (ESCAPE_SET_LIT, 300), (DAMP_FLOOR_LIT, 1)+               , (STAIR_TERMINAL_LIT, 20), (LIFT_TERMINAL_LIT, 6) ]+  , tfeature = ChangeWith ProjYes [(1, OIL_SOURCE)] S_OIL_SPILL  -- oil floats+               : ChangeWith ProjYes [(1, COLD_SOURCE)] S_FROZEN_PATH+               : ChangeWith ProjYes [(5, WATER_SOURCE)] S_SHALLOW_WATER_LIT+               : tfeature floorArena+  }+floorDirt = floorArena+  { tname    = "dirt floor"+  , tfreq    = [ (BRAWL_SET_LIT, 900), (SHOOTOUT_SET_LIT, 900)+               , (HUNT_SET_LIT, 900), (ESCAPE_SET_LIT, 700)+               , (AMBUSH_SET_LIT, 1000), (BATTLE_SET_LIT, 500)+               , (DIRT_LIT, 1) ]+  }+floorDirtSpice = floorDirt+  { tfreq    = [ (TREE_SHADE_WALKABLE_LIT, 1), (BUSH_CLUMP_LIT, 1)+               , (UNDERBRUSH_CLUMP_LIT, 1), (PUMPS_LIT, 100) ]+  , tfeature = Spice : tfeature floorDirt+  }+floorActor = floorArena+  { tfreq    = [(S_FLOOR_ACTOR_LIT, 1)]+  , tfeature = OftenActor : tfeature floorArena+  }+floorActorItem = floorActor+  { tfreq    = [ (FLOOR_ACTOR_ITEM, 1), (FLOOR_ACTOR_ITEM_LIT, 1)+               , (LEGEND_LIT, 100) ]+  , tfeature = VeryOftenItem : tfeature floorActor+  }+floorAshes = floorActor  -- always lit+  { tfreq    = [ (SMOKE_CLUMP_LIT, 1), (SMOKE_CLUMP_DARK, 1)+               , (S_FLOOR_ASHES_LIT, 1), (S_FLOOR_ASHES_DARK, 1)+               , (ASHES_SMOKE_LIT, 2), (ASHES_SMOKE_DARK, 5)+               , (RUBBLE_BURNING_OR_NOT, 25) ]+  , tname    = "dirt and ash pile"+  , tcolor   = Brown+  , tcolor2  = Brown+  }+shallowWater = TileKind+  { tsymbol  = '~'+  , tname    = "water puddle"+  , tfreq    = [ (AQUATIC, 1), (S_SHALLOW_WATER_LIT, 1), (LEGEND_LIT, 100)+               , (EMPTY_SET_LIT, 20), (NOISE_SET_LIT, 30), (SHOOTOUT_SET_LIT, 5)+               , (HUNT_SET_LIT, 250), (LIFT_TERMINAL_LIT, 4) ]+  , tcolor   = BrCyan+  , tcolor2  = Cyan+  , talter   = 2  -- projectiles won't trigger embeds+  , tfeature = Embed SHALLOW_WATER+                 -- crafting via embed first, transformations a fallback+               : ChangeWith ProjYes [(1, OIL_SOURCE)] S_OIL_SPILL  -- oil floats+               : ChangeWith ProjYes [(1, COLD_SOURCE)] S_FROZEN_PATH+               : tfeature floorActor+      -- can't make fog from water, because air would need to be cool, too;+      -- if concealment needed, make smoke from fire instead+  }+shallowWaterSpice = shallowWater+  { tfreq    = [ (FOG_CLUMP_LIT, 40), (PUMPS_LIT, 200)+               , (RUBBLE_OR_WASTE_LIT, 1) ]+  , tfeature = Spice : tfeature shallowWater+  }+shallowWater2 = shallowWater+  { tname    = "water pool"+  , tfreq    = [(S_POOL_LIT, 1)]+  }+floorRed = floorCorridor  -- always lit+  { tname    = "emergency walkway"+  , tfreq    = [(TRAIL_LIT, 50), (SAFE_TRAIL_LIT, 50)]+  , tcolor   = BrRed+  , tcolor2  = Red+  , tfeature = [ ChangeWith ProjYes [(1, OIL_SOURCE)] S_OIL_SPILL+                   -- non-porous enough+               , Embed STRAIGHT_PATH+               , Trail, Walkable, Clear ]+  }+floorBlue = floorRed  -- always lit+  { tname    = "frozen path"+  , tfreq    = [(TRAIL_LIT, 50), (S_FROZEN_PATH, 1)]+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , talter   = 0+  , tfeature = [ ChangeWith ProjYes [(1, FIRE_SOURCE)] S_SHALLOW_WATER_LIT+               , ChangeWith ProjYes [(1, OIL_SOURCE)] S_OIL_SPILL+                   -- non-porous enough+               , Embed FROZEN_GROUND+               , Trail, Walkable, Clear ]+  }+floorBrown = floorRed  -- always lit+  { tname    = "transport route"+  , tfreq    = [ (TRAIL_LIT, 50), (SAFE_TRAIL_LIT, 50)+               , (TRANSPORT_ROUTE, 1) ]+  , tcolor   = BrMagenta+  , tcolor2  = Magenta+  }+floorArenaShade = floorActor  -- always dark+  { tname    = "shaded ground"+  , tfreq    = [ (S_SHADED_GROUND, 1)+               , (TREE_SHADE_WALKABLE_LIT, 2), (TREE_SHADE_WALKABLE_DARK, 2)]+  , tcolor   = BrYellow  -- match others, even though no lit counterpart+  , tcolor2  = BrBlack+  , tfeature = Dark : NoItem : tfeature floorActor+  }++-- * Allure-specific++-- ** Not walkable++-- *** Not clear++oriel = TileKind+  { tsymbol  = '%'  -- story-wise it's transparent, hence the symbol+  , tname    = "oriel"+  , tfreq    = [ (ORIELS_FENCE, 15)+               , (AIRLOCK_FENCE, 5), (EMPTY_AIRLOCK_FENCE, 5) ]+  , tcolor   = White+  , tcolor2  = Black+  , talter   = 5+  , tfeature = [Embed BLACK_STARRY_SKY, Dark]+  }+outerHullWall = basicOuterFence+  { tname    = "outer hull wall"+  , tfreq    = [ (S_BASIC_OUTER_FENCE, 1), (ORIELS_FENCE, 85)+               , (AIRLOCK_FENCE, 40), (EMPTY_AIRLOCK_FENCE, 40) ]+  }+rubbleBurning = TileKind  -- present in EMPTY_SET_LIT as early light/fire source+  { tsymbol  = '&'+  , tname    = "burning installation"+  , tfreq    = [ (EMPTY_SET_LIT, 2), (POWER_SET_DARK, 20)+               , (AMBUSH_SET_DARK, 15), (ZOO_SET_DARK, 30)+               , (STAIR_TERMINAL_LIT, 4), (STAIR_TERMINAL_DARK, 4)+               , (LIFT_TERMINAL_LIT, 4), (LIFT_TERMINAL_DARK, 4)+               , (S_BURNING_INSTALLATION, 1), (RUBBLE_BURNING_OR_NOT, 25) ]+  , tcolor   = BrRed+  , tcolor2  = Red+  , talter   = 4  -- boss can dig through+  , tfeature = [ OpenWith ProjYes [(3, WATER_SOURCE)] S_SMOKE_LIT+               , OpenWith ProjYes [(1, BLAST_SOURCE)] ASHES_SMOKE_LIT+               , Embed BIG_FIRE  -- not as tall as a tree, so quenchable+               , ChangeWith ProjNo [(1, FIREPROOF_CLOTH)] S_RUBBLE_PILE+                   -- full effects experienced, but rubble saved for repeat+               , OpenWith ProjNo [] RUBBLE_BURNING_OR_NOT ]+                   -- no pathfinding through+  }+rubbleBurningSpice = rubbleBurning+  { tfreq    = [ (SMOKE_CLUMP_LIT, 1), (SMOKE_CLUMP_DARK, 1)+               , (CACHE_DEPOSIT, 33) ]+  , tfeature = Spice : tfeature rubbleBurning+  }+wallOpenable = bedrock+  { tfreq    = [(OPENABLE_WALL, 1)]+  , tfeature = [BuildAs S_CLOSED_DOOR]  -- when ending a corridor, have doors+  }+wallObscuredSafety = TileKind+  { tsymbol  = '#'+  , tname    = "safety procedures board"+  , tfreq    = [(OBSCURED_WALL, 4), (EXIT_SET_LIT, 1)]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 5+  , tfeature = [ Embed RUINED_FIRST_AID_KIT+               , HideAs S_SUSPECT_WALL+               ]+  }+signboardReadExtinguisher = TileKind+  { tsymbol  = '0'+  , tname    = "fire extinguisher cabinet"+  , tfreq    = [ (SIGNBOARD, 20)+               , (LIFT_TERMINAL_LIT, 10), (LIFT_TERMINAL_DARK, 10)+               , (EMPTY_SET_LIT, 7), (ARENA_SET_LIT, 1), (ARENA_SET_DARK, 2)+               , (MUSEUM_SET_DARK, 1), (EXIT_SET_LIT, 1), (NOISE_SET_LIT, 1)+               , (ESCAPE_SET_DARK, 1), (AMBUSH_SET_DARK, 1) ]+  , tcolor   = BrCyan+  , tcolor2  = Cyan+  , talter   = 5+  , tfeature = [ Embed FIRE_FIGHTING_GEAR+               , HideAs S_SIGNBOARD_UNREAD+               ]+  }+wallObscured3dBillboard = TileKind+  { tsymbol  = '#'+  , tname    = "3D billboard"+  , tfreq    = [(OBSCURED_WALL, 15)]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 5+  , tfeature = [ Embed DISPLAY_3D+               , HideAs S_SUSPECT_WALL+               ]+  }+wallObscuredPipework = TileKind+  { tsymbol  = '#'+  , tname    = "exposed pipework"+  , tfreq    = [(OBSCURED_WALL, 25)]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 5+  , tfeature = [ Embed CRACKED_FLUE+               , HideAs S_SUSPECT_WALL+               ]+  }+wallObscuredScary = TileKind+  { tsymbol  = '#'+  , tname    = "stained wall"+  , tfreq    = [(OBSCURED_WALL, 35)]+  , tcolor   = BrWhite+  , tcolor2  = defFG+  , talter   = 5+  , tfeature = [ Embed BLOOD_ON_WALL+               , HideAs S_SUSPECT_WALL+               ]+  }+liftShaft = pillar+  { tname    = "lift shaft"+  , tfreq    = [(S_LIFT_SHAFT, 1)]+  }+rock = pillar+  { tname    = "rock outcrop"+  , tfreq    = [(ARENA_SET_LIT, 6), (BRAWL_SET_LIT, 30)]+  }+pillarCache2 = pillarCache+  { tname    = "rack of deposit boxes"+  , tfreq    = [ (CACHE_DEPOSIT_OR_NOT, 33)+               , (CACHE_DEPOSIT_BREACHED, 1) ]+  , tfeature = [ Embed DEPOSIT_BOX+               , ChangeTo CACHE_DEPOSIT_OR_NOT+               , ConsideredByAI ]+  }+pillarCache3 = pillarCache+  { tname    = "rack of sealed deposit boxes"+  , tfreq    = [ (CACHE_DEPOSIT, 33), (ARENA_SET_DARK, 4)+               , (STAIR_TERMINAL_LIT, 1), (STAIR_TERMINAL_DARK, 1) ]+  , tfeature = [ ChangeWith ProjNo [(1, BREACHING_TOOL)] CACHE_DEPOSIT_BREACHED+                   -- @BLAST_SOURCE@ not enough+               , ConsideredByAI ]+  }+pillarCache4 = pillarCache+  { tname    = "jewelry display"+  , tfreq    = [(CACHE_JEWELRY_OR_NOT, 40)]+  , tfeature = [ Embed JEWELRY_CASE+               , ChangeTo CACHE_JEWELRY_OR_NOT+               , ConsideredByAI ]+  }+pillarCache5 = pillarCache+  { tname    = "jewelry display"+  , tfreq    = [ (CACHE_JEWELRY_TRAPPED_OR_NOT, 40), (CACHE_JEWELRY, 60)+               , (MUSEUM_SET_DARK, 2) ]+  , tfeature = [ Embed JEWELRY_CASE+               , ChangeWith ProjNo [(1, COLD_SOURCE)]+                            CACHE_JEWELRY_TRAPPED_OR_NOT+                   -- halts watchdog+               , ChangeWith ProjNo [(1, WIRECUTTING_TOOL)] CACHE_JEWELRY_OR_NOT+                   -- disarms trap altogether+               , Embed JEWELRY_DISPLAY_TRAP+               , ChangeTo CACHE_JEWELRY_TRAPPED_OR_NOT ]+               -- not ConsideredByAI, to leave the risk and reward to the player+  }+stairsTrappedDownOil = TileKind+  { tsymbol  = '>'+  , tname    = "slippery staircase down"+  , tfreq    = [(STAIRCASE_DOWN, 5), (S_STAIRCASE_TRAP_DOWN_OIL, 1)]+  , tcolor   = BrRed+  , tcolor2  = Red+  , talter   = talterForStairs+  , tfeature = [ ChangeWith ProjNo [(1, THICK_CLOTH)] ORDINARY_STAIRCASE_DOWN+                   -- safely soaks oil; marginal --- no warning if fails+               , Embed STAIRS_DOWN+               , Embed STAIRS_TRAP_DOWN_OIL+               , ChangeTo ORDINARY_STAIRCASE_DOWN+               , ConsideredByAI ]+ }+stairsDecontaminatingUp = stairsUp+  { tname    = "decontaminating staircase up"+  , tfreq    = [(DECON_STAIRCASE_UP, 1)]+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , talter   = talterForStairs+  , tfeature = Embed DECONTAMINATION_CHAMBER : tfeature stairsUp+  }+stairsWelded = stairsUp+  { tname    = "staircase up welded shut"+  , tfreq    = [(WELDED_STAIRCASE_UP, 1)]+  , tcolor   = BrMagenta+  , tcolor2  = Magenta+  , talter   = talterForStairs + 3  -- gear or level up needed+  , tfeature = [ Embed S_CRUDE_WELD+                   -- the embed goes first, because the embed is marginal here+               , ChangeWith ProjNo [(1, COLD_SOURCE)] ORDINARY_STAIRCASE_UP+               , ChangeWith ProjNo [(1, BLOWTORCH)] ORDINARY_STAIRCASE_UP+               , ConsideredByAI ]+  }+stairsLiftUp = stairsUp  -- fireproof+  { tname    = "lift up"+  , tfreq    = [(STAIRCASE_LIFT_UP, 9), (ORDINARY_LIFT_UP, 1)]+  , talter   = talterForStairs+  , tcolor   = BrCyan+  , tcolor2  = Cyan+  , tfeature = [Embed LIFT_UP, ConsideredByAI]+  }+stairsLiftTrappedUp = stairsTrappedUp+  { tname    = "corroded lift up"+  , tfreq    = [(STAIRCASE_LIFT_UP, 1)]+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , tfeature = [ Embed LIFT_UP+               , Embed LIFT_TRAP+               , ConsideredByAI, ChangeTo ORDINARY_LIFT_UP ]+                 -- AI uses despite the trap; exploration more important+  }+stairsLiftGatedUp = stairsLiftUp+  { tname    = "manually opened lift up"+  , tfreq    = [(GATED_LIFT_UP, 1)]+  , talter   = talterForStairs + 2  -- animals and bosses can't use+  }+stairsLiftDecontaminatingUp = stairsLiftUp+  { tname    = "decontaminating lift up"+  , tfreq    = [(DECON_LIFT_UP, 1)]+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , tfeature = Embed DECONTAMINATION_CHAMBER : tfeature stairsLiftUp+  }+stairsLiftWelded = stairsLiftUp+  { tname    = "lift up welded shut"+  , tfreq    = [(WELDED_LIFT_UP, 1)]+  , tcolor   = BrMagenta+  , tcolor2  = Magenta+  , talter   = talterForStairs + 3  -- gear or level up needed+  , tfeature = [ Embed S_CRUDE_WELD+                   -- the embed goes first, because marginal+               , ChangeWith ProjNo [(1, COLD_SOURCE)] ORDINARY_LIFT_UP+               , ChangeWith ProjNo [(1, BLOWTORCH)] ORDINARY_LIFT_UP+               , ConsideredByAI ]+  }+stairsDecontaminatingDown = stairsDown+  { tname    = "decontaminating staircase down"+  , tfreq    = [(DECON_STAIRCASE_DOWN, 1)]+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , talter   = talterForStairs+  , tfeature = Embed DECONTAMINATION_CHAMBER : tfeature stairsDown+  }+stairsLiftDown = stairsDown+  { tname    = "lift down"+  , tfreq    = [(STAIRCASE_LIFT_DOWN, 9), (ORDINARY_LIFT_DOWN, 1)]+  , tcolor   = BrCyan+  , tcolor2  = Cyan+  , talter   = talterForStairs+  , tfeature = [Embed LIFT_DOWN, ConsideredByAI]+  }+stairsLiftTrappedDown = stairsTrappedDown+  { tname    = "corroded lift down"+  , tfreq    = [(STAIRCASE_LIFT_DOWN, 1)]+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , tfeature = [ Embed LIFT_DOWN+               , Embed LIFT_TRAP+               , ConsideredByAI, ChangeTo ORDINARY_LIFT_DOWN ]+  }+stairsLiftGatedDown = stairsLiftDown+  { tname    = "manually opened lift down"+  , tfreq    = [(GATED_LIFT_DOWN, 1)]+  , talter   = talterForStairs + 2  -- animals and bosses can't use+  }+stairsLiftDecontaminatingDown = stairsLiftDown+  { tname    = "decontaminating lift down"+  , tfreq    = [(DECON_LIFT_DOWN, 1)]+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , tfeature = Embed DECONTAMINATION_CHAMBER : tfeature stairsLiftDown+  }+escapeSpaceshipDown = escapeDown+  { tname    = "airlock to a shuttle"+  , tfreq    = [(ESCAPE_SPACESHIP_DOWN, 1), (AIRLOCK_FENCE, 3)]+  }+emptyAirlock = escapeDown+  { tname    = "empty airlock"+  , tfreq    = [ (AIRLOCK_FENCE, 2), (EMPTY_AIRLOCK_FENCE, 7)+               , (EMPTY_SET_LIT, 8), (AMBUSH_SET_DARK, 7) ]+                   -- not in exitSetLit; space can't be seen+  , tcolor   = BrBlack+  , tcolor2  = BrBlack+  , tfeature = [Embed DISENGAGED_DOCKING_GEAR]+  }+reinforcedWall = TileKind+  { tsymbol  = '#'+  , tname    = "reinforced wall"+  , tfreq    = [(S_REINFORCED_WALL, 1), (ROGUE_SET, 15), (EXIT_SET_LIT, 20)]+  , tcolor   = White+  , tcolor2  = BrBlack+  , talter   = 100+  , tfeature = []+  }+reinforcedWallSpice = reinforcedWall+  { tfreq    = [ (DOORLESS_WALL, 20), (CACHE_MAZE, 33)+               , (CACHE_ABANDONED_OR_NOT, 60), (CACHE_DEPOSIT_OR_NOT, 66) ]+  , tfeature = Spice : tfeature reinforcedWall+  }+wallShuttle = bedrock+  { tname    = "shuttle hull"+  , tfreq    = [(S_SHUTTLE_HULL, 1)]+  , tfeature = [Embed SHUTTLE_HARDWARE]+  }+wallShuttleSpice = wallShuttle+  { tfreq    = [(CACHE_SHUTTLE, 75)]+  , tfeature = Spice : tfeature wallShuttle+  }+doorStuck = TileKind+  { tsymbol  = '+'+  , tname    = "stuck door"+  , tfreq    = [(STUCK_DOOR, 1)]+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , talter   = 2+  , tfeature = [ OpenWith ProjNo [(1, BREACHING_TOOL)] S_OPEN_DOOR+               , OpenWith ProjYes [(1, BLAST_SOURCE)] S_OPEN_DOOR+               , Embed DOOR_TRAP_PUSH+               , OpenWith ProjNo [] S_OPEN_DOOR ]  -- no pathfinding+  }+barrel = TileKind+  { tsymbol  = '0'+  , tname    = "large barrel"+  , tfreq    = [ (BARREL, 1)+               , (EXIT_SET_LIT, 3), (NOISE_SET_LIT, 20), (POWER_SET_DARK, 50)+               , (ZOO_SET_DARK, 30), (AMBUSH_SET_DARK, 2) ]+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , talter   = 0  -- projectiles can trigger the embed+  , tfeature = [ Embed BARREL_CONTENTS+               , OpenWith ProjYes [] ASHES_SMOKE_LIT ]+                   -- no pathfinding through+  }+barrelSpice = barrel+  { tfreq    = [ (SMOKE_CLUMP_LIT, 1), (SMOKE_CLUMP_DARK, 1)+               , (RUBBLE_OR_WASTE_LIT, 2), (RUBBLE_OR_WASTE_DARK, 2)+               , (CACHE_MAZE, 33) ]+  , tfeature = Spice : tfeature barrel+  }++-- *** Clear++machineWall = TileKind+  { tsymbol  = '%'+  , tname    = "hardware rack"+  , tfreq    = [ (S_HARDWARE_RACK, 1)+               , (ROGUE_SET, 25), (NOISE_SET_LIT, 250), (POWER_SET_DARK, 250)+               , (EXIT_SET_LIT, 30)+               , (LIFT_TERMINAL_LIT, 40), (LIFT_TERMINAL_DARK, 40) ]+  , tcolor   = White+  , tcolor2  = BrBlack+  , talter   = 100+  , tfeature = [Clear]+  }+machineWallSpice = machineWall+  { tfreq    = [(DOORLESS_MACHINERY, 1)]+  , tfeature = Spice : tfeature machineWall+  }+bushEdible = TileKind+  { tsymbol  = '%'+  , tname    = "ripe bush"+  , tfreq    = [ (EMPTY_SET_LIT, 4), (ARENA_SET_LIT, 2), (ARENA_SET_DARK, 4)+               , (SHOOTOUT_SET_LIT, 1), (HUNT_SET_LIT, 1)+               , (ESCAPE_SET_DARK, 4), (ZOO_SET_DARK, 1)+               , (LIFT_TERMINAL_LIT, 1), (LIFT_TERMINAL_DARK, 1) ]+  , tcolor   = BrMagenta+  , tcolor2  = Magenta+  , talter   = 4+  , tfeature = [ Clear+               , Embed EDIBLE_PLANT_RIPE+                   -- loot granted even when ignited, but missiles can't reap+               , ChangeWith ProjYes [(1, FIRE_SOURCE)] S_BURNING_BUSH+               , ChangeTo S_BUSH_LIT ]+  }+bushEdibleSpice = bushEdible+  { tfreq    = [ (BUSH_GROVE_LIT, 1), (BUSH_GROVE_DARK, 1)+               , (PUMPS_LIT, 100), (PUMPS_DARK, 100) ]+  , tfeature = Spice : tfeature bushEdible+  }++-- ** Walkable++-- *** Not clear++underbrushBurning = TileKind  -- always lit+  { tsymbol  = ';'+  , tname    = "burning underbrush"+  , tfreq    = [ (AMBUSH_SET_DARK, 1), (ZOO_SET_DARK, 5)+               , (S_BURNING_UNDERBRUSH, 1) ]+  , tcolor   = BrRed+  , tcolor2  = Red+  , talter   = 2  -- due to this, ordinary projectiles can't put out the fire+                  -- and don't get burned+  , tfeature = [ Walkable, NoItem, NoActor  -- not clear, due to smoke+               , Embed SMALL_FIRE  -- little mass, so one fire only+               , ChangeWith ProjYes [(1, WATER_SOURCE)] S_SMOKE_LIT+                   -- transformation only a fallback if crafting unintended+               , ChangeWith ProjNo [(1, FIREPROOF_CLOTH)] S_UNDERBRUSH_LIT+                   -- underbrush saved for repeat+               , ChangeTo S_FLOOR_ASHES_LIT ]  -- not enough matter for smoke+  }++-- *** Clear++floorOily = floorArena+  { tname    = "oily floor"+  , tfreq    = [ (MUSEUM_SET_LIT, 40), (POWER_SET_LIT, 550), (EXIT_SET_LIT, 800)+               , (BATTLE_SET_LIT, 1000)+               , (OILY_FLOOR_LIT, 1), (RUBBLE_OR_WASTE_LIT, 1)+               , (OIL_RESIDUE_LIT, 4) ]+  , tfeature = ChangeWith ProjYes [(1, OIL_SOURCE)] S_OIL_SPILL+                 -- non-porous enough+               : tfeature floorArena+  }+oilSpill = TileKind  -- always lit+  { tsymbol  = '~'+  , tname    = "oil spill"+  , tfreq    = [ (POWER_SET_DARK, 35), (EXIT_SET_LIT, 9)+               , (AMBUSH_SET_DARK, 20), (S_OIL_SPILL, 1) ]+  , tcolor   = BrYellow+  , tcolor2  = BrGreen+  , talter   = 2  -- projectiles won't trigger embeds; doesn't matter+                  -- for others, because walkable;+                  -- TODO: not everything should be able/willing to enter+  , tfeature = [ Embed OIL_PUDDLE+                   -- embed goes first, because transformation and crafting+                   -- may use the same item and crafting needs priority+               , ChangeWith ProjYes [(1, FIRE_SOURCE)] S_BURNING_OIL+               , ChangeWith ProjNo [(1, THICK_CLOTH)] OILY_FLOOR_LIT+               , Walkable, Clear ]+  }+oilSpillSpice = oilSpill+  { tfreq    = [ (RUBBLE_OR_WASTE_LIT, 1), (RUBBLE_OR_WASTE_DARK, 1)+               , (OIL_RESIDUE_LIT, 1), (OIL_RESIDUE_DARK, 1) ]+  , tfeature = Spice : tfeature oilSpill+  }++oilBurning = TileKind  -- always lit+  { tsymbol  = '~'+  , tname    = "burning oil"+  , tfreq    = [(POWER_SET_DARK, 1), (AMBUSH_SET_DARK, 1), (S_BURNING_OIL, 1)]+  , tcolor   = BrRed+  , tcolor2  = Red+  , talter   = 2  -- due to this, projectiles can't put out the fire+                  -- and don't get burned+  , tfeature = [ Walkable, Clear  -- clear, no smoke, as in oil lamps+               , NoItem, NoActor+               , Embed SMALL_FIRE_5+               , Embed OIL_PUDDLE+               , ChangeWith ProjNo [(1, FIREPROOF_CLOTH)] OILY_FLOOR_LIT+                   -- safely soaks oil, if crafting fails or unintended+               , ChangeTo S_OIL_SPILL ]+                   -- TODO: change only after all 5 fires used up+  }+floorWindow = floorArena  -- always lit+  { tsymbol  = ' '  -- story-wise it's transparent, hence the symbol+  , tname    = "floor window"+  , tfreq    = [(EMPTY_SET_LIT, 24)]+  , tcolor   = defFG+  , tcolor2  = defFG+  , tfeature = Embed BLACK_STARRY_SKY : tfeature floorCorridor+  }+underbrush = TileKind  -- always lit+  { tsymbol  = floorSymbol+  , tname    = "underbrush"+  , tfreq    = [ (S_UNDERBRUSH_LIT, 1), (S_UNDERBRUSH_DARK, 1)+               , (UNDERBRUSH_CLUMP_LIT, 1), (UNDERBRUSH_CLUMP_DARK, 1)+               , (EMPTY_SET_LIT, 200), (ARENA_SET_LIT, 80), (BRAWL_SET_LIT, 100)+               , (SHOOTOUT_SET_LIT, 100), (HUNT_SET_LIT, 100)+               , (ESCAPE_SET_LIT, 100), (ZOO_SET_DARK, 5)+               , (AMBUSH_SET_DARK, 20)+               , (BUSH_CLUMP_LIT, 1), (BUSH_CLUMP_DARK, 1)+               , (TRAIL_LIT, 50), (SAFE_TRAIL_LIT, 50) ]+  , tcolor   = BrGreen+  , tcolor2  = Green+  , talter   = 0+  , tfeature = [ ChangeWith ProjYes [(1, FIRE_SOURCE)] S_BURNING_UNDERBRUSH+               , Trail, Walkable, Clear, NoItem ]+  }+workshop = TileKind  -- always lit+  { tsymbol  = ':'+  , tname    = "workshop"+  , tfreq    = [ (LEGEND_LIT, 100), (LEGEND_DARK, 100)+               , (EMPTY_SET_LIT, 16), (SHOOTOUT_SET_LIT, 2)+               , (AMBUSH_SET_DARK, 4), (BATTLE_SET_DARK, 4) ]+  , tcolor   = BrBlue+  , tcolor2  = Blue+  , talter   = 2  -- projectiles cannot craft (otherwise durable tools+                  -- would be applicable without harmful side-effects)+  , tfeature = [ Walkable, NoItem, NoActor  -- not clear, due to overhang+               , Embed WORKSHOP_BENCH ]+  }++-- * Helper functions++makeDark :: TileKind -> TileKind+makeDark k = let darkenText :: GroupName TileKind -> GroupName TileKind+                 darkenText t = maybe t (GroupName . (<> "Dark"))+                                $ T.stripSuffix "Lit" $ fromGroupName t+                 darkenFreq :: (GroupName TileKind, Int)+                            -> [(GroupName TileKind, Int)]+                 darkenFreq (t, n) =+                   case T.stripSuffix "Lit" $ fromGroupName t of+                     Nothing -> [(t, n)]+                     Just stripped ->+                       let dark = GroupName $ stripped <> "Dark"+                       in -- lit plays the role of dark in a @Dark@ group+                          [(dark, n) | isNothing $ dark `lookup` darkFreq]+                 (darkFreq, notDarkFreq) =+                   partition (T.isSuffixOf "Dark" . fromGroupName . fst)+                             (tfreq k)+                 darkFrequency :: Freqs TileKind+                 darkFrequency = concatMap darkenFreq notDarkFreq+                 darkFeat (OpenTo t) = Just $ OpenTo $ darkenText t+                 darkFeat (CloseTo t) = Just $ CloseTo $ darkenText t+                 darkFeat (ChangeTo t) = Just $ ChangeTo $ darkenText t+                 darkFeat (OpenWith proj grps t) =+                   Just $ OpenWith proj grps $ darkenText t+                 darkFeat (CloseWith proj grps t) =+                   Just $ CloseWith proj grps $ darkenText t+                 darkFeat (ChangeWith proj grps t) =+                   Just $ ChangeWith proj grps $ darkenText t+                 darkFeat (HideAs t) = Just $ HideAs $ darkenText t+                 darkFeat (BuildAs t) = Just $ BuildAs $ darkenText t+                 darkFeat (RevealAs t) = Just $ RevealAs $ darkenText t+                 darkFeat (ObscureAs t) = Just $ ObscureAs $ darkenText t                  darkFeat VeryOftenItem = Just OftenItem                  darkFeat OftenItem = Nothing  -- items not common in the dark                  darkFeat feat = Just feat
− GameDefinition/InGameHelp.txt
@@ -1,184 +0,0 @@-This is a snapshot of in-game help, rendered with default config file.-For more general gameplay information see-https://github.com/AllureOfTheStars/Allure/blob/master/GameDefinition/PLAYING.md---Minimal cheat sheet for casual play.-- 7 8 9        Walk throughout a level with the mouse or the numeric keypad-  \|/         (top diagram) or the Vi editor keys (bottom) or with a compact- 4-5-6        laptop setup (middle) that needs to be enabled in config.ui.ini.-  /|\- 1 2 3        Run until disturbed with Shift or Control. Go-to a position-              with LMB (left mouse button). Run collectively with S-LMB-              (press the button while holding Shift).- 7 8 9-  \|/         In aiming mode, the same keys (and mouse) move the x-hair- u-i-o        (aiming crosshair). Press `KP_5` (`5` on keypad) to wait,-  /|\         bracing for impact, which reduces any damage taken- j k l        and prevents displacement by foes. Press `C-KP_5` or `C-KP_5`-              (the same key with Shift or Control) to lurk 0.1 of a turn,-              without bracing.- y k u-  \|/         You displace enemies by running into them with Shift/Control- h-.-l        or S-LMB. Search, open, descend and attack by bumping into-  /|\         walls, doors, stairs and enemies. The best item to attack with- b j n        is automatically chosen from among melee weapons in your-              personal equipment and your body parts.-- The following commands, joined with the basic set above,- let you accomplish anything in the game, though- not necessarily with the fewest keystrokes. You can also- play the game exclusively with a mouse, or both mouse- and keyboard. (See the ending help screens for mouse commands.)- Lastly, you can select a command with arrows or mouse directly- from the help screen or the dashboard and execute it on the spot.-- keys         command- E            manage equipment of the leader- g or ,       grab item(s)- ESC          open main menu/finish aiming- RET or INS   open dashboard/accept target- SPACE        clear messages and show history- S-TAB        cycle among all party members- KP_* or !    cycle x-hair among enemies- KP_/ or /    cycle x-hair among items- c            close door- %            yell/yawn---Here is the complete list of commands from the snapshot of in-game help.-For more general gameplay information see-https://github.com/AllureOfTheStars/Allure/blob/master/GameDefinition/PLAYING.md--Item menu commands.-- keys         command- g or ,       grab item(s)- d or .       drop item(s)- f            fling projectile- C-f          auto-fling and keep choice- a            apply consumable- C-a          apply and keep choice- p or i       pack item- e            equip item- s            stash and share item--Remaining item-related commands.-- keys         command- E            manage equipment of the leader- P or I       manage inventory pack of the leader- S            manage the shared party stash- G            manage items on the ground- A            manage all owned items- @            describe organs of the leader- #            show skill summary of the leader- ~            display known lore-- Note how lower case item commands (pack an item, equip, stash)- let you move items into a particular item store, while- upper case item commands (manage Pack, Equipment, Stash, etc.)- let you view and organize items within an item store.- Once a store management menu is opened, you can switch- stores at will, so the multiple commands only determine- the starting item store. Each store is accessible- from the dashboard as well.--All terrain exploration and alteration commands.-- keys         command- TAB          cycle among party members on the level- S-TAB        cycle among all party members- c            close door- C            open or close or alter- =            select (or deselect) party member- _            deselect (or select) all on the level- ;            go to x-hair for 25 steps- :            run to x-hair collectively for 25 steps- x            explore nearest unknown spot- X            autoexplore 25 times- R            rest (wait 25 times)- C-R          heed (lurk 0.1 turns 100 times)- 0, 1 ... 6   pick a particular actor as the new leader--Aiming commands.-- keys         command- ESC          open main menu/finish aiming- RET or INS   open dashboard/accept target- KP_* or !    cycle x-hair among enemies- KP_/ or /    cycle x-hair among items- +            swerve the aiming line- -            unswerve the aiming line- \            cycle aiming modes- C-?          set x-hair to nearest unknown spot- C-/          set x-hair to nearest item- C-{          set x-hair to nearest upstairs- C-}          set x-hair to nearest dnstairs- <            move aiming one level up- >            move aiming one level down- BACKSPACE    clear chosen item and x-hair--Mouse commands.-- Screen area and UI mode (exploration/aiming) determine- mouse click effects. First, we give an overview- of effects of each button over the game map area.- The list includes not only left and right buttons, but also- the optional middle mouse button (MMB) and the mouse wheel,- which is also used over menus, to page-scroll them.- (For mice without RMB, one can use Control key with LMB and for mice- without MMB, one can use C-RMB or C-S-LMB.)- Next we show mouse button effects per screen area,- in exploration mode and (if different) in aiming mode.-- keys         command- LMB          go to pointer for 25 steps/fling at enemy- S-LMB        run to pointer collectively for 25 steps/fling at enemy- RMB or C-LMB start aiming at enemy under pointer- S-RMB        open or close or alter at pointer- MMB or C-RMB snap x-hair to floor under pointer- WHEEL-UP     swerve the aiming line- WHEEL-DN     unswerve the aiming line-- exploration    LMB (left mouse button)          RMB (right mouse button)- message line   clear messages and show history  display help- leader on map  grab item(s)                     drop item(s)- party on map   pick new leader on screen        select party member on screen- the map area   go to pointer for 25 steps       set x-hair to enemy- level number   move aiming one level up         move aiming one level down- level caption  open dashboard                   open main menu- percent seen   explore nearest unknown spot     autoexplore 25 times- x-hair info    cycle x-hair among enemies       cycle x-hair among items- party roster   pick new leader on screen        select party member on screen- Calm value     yell/yawn                        yell/yawn- HP gauge       rest (wait 25 times)             heed (lurk 0.1 turns 100 times)- HP Value       wait a turn, bracing for impact  lurk 0.1 of a turn- leader info    auto-fling and keep choice       clear chosen item and x-hair-- aiming mode    LMB (left mouse button)          RMB (right mouse button)- the map area   fling at enemy under pointer     snap x-hair to enemy- level caption  accept target                    cancel aiming- percent seen   set x-hair to nearest upstairs   set x-hair to nearest dnstairs--Assorted commands.-- keys         command- SPACE        clear messages and show history- %            yell/yawn- C-s          start new game- C-x          save and exit to desktop- C-t          toggle autoplay (insert coin)- C-q          quit game and start autoplay- C-c          exit to desktop without saving- ?            display help- F1           display help immediately- F12          open dashboard- v            voice again the recorded commands- V            voice recorded commands 100 times- C-v          voice recorded commands 1000 times- C-V          voice recorded commands 25 times- '            start recording commands- C-S          save game backup- C-P          print screen
GameDefinition/Main.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE PackageImports #-} -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE.
− GameDefinition/MainMenu.ascii
@@ -1,45 +0,0 @@-                                                                                -                                                                                -                                                                                -                                                                                -                                                  ·                             -                                                  ··                            -        Allure of the Stars                      ······· ·                      -                                           ········.···········                 -      <allureofthestars.com>            ···········..·············              -                                      ·············..···················        -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  ········....--:---.....···········         -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  ·····..--::://+////:::--..··.-····         -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  ··..--:////////::://////:-.-/+-·····       -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  ·.-:////:---.-:....---:///::::.······      -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  .-://:--......-........--://:-..······     -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  ://:-.........:..........-:///:..······    -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  //:-....-.----:------......-://:-.·····    -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  .-......---:::/:::::----....-://:..······· -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  -....----:////++////::---....-://-....···  -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  .....--::/+++oooooo+//::--....-:+/:..····  -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  ....--://+oosyhhyysoo+/::--..--://:.······ -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  ...---:/+osyhdmdddhyso+/:::--..-:+:-.····· -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  ..--://+oshdmmNNNNmdhso+/:---...-//-.····· -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  ..--:/+osydmNNNNNNNmdyo+/::-....-//:.····· -{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  ..--:/+osydNNNNNNNNmdyo+/::-....-//:.····· -                        ·······-/+/-----:://+sydmmNNNNNNmdyo+//:--...-//:.····· -                        ·······.://-...--::/osyhdmmNNNNmdhso+/:--....-//-.····· -                         ······.-//-...-::/+ooyhhhdmmddhyso+//::-....:+/-.····· -                        ·······.-:+:----://+osyyyyyhhyysso++/:----..-//:.······ -                         ·······.-/+::::::/+oossssssoooo++//::-....-:+/-.······ -                          ·····-.-:++/:::///+++oooo+++////::---....-//:-.·····  -                            ·····.-/++//////++++++//:::::----...---//:-.······  -                              ···.-:+o++/+++//////::------......-://:-.······   -                               ···.-//+o++++//::::---.........--://:-.······    -                               ····+-//+oo++//:::--.........--://:-..······     -                               ···-.--+//+oo+///:--.....---:////:-.·······      -                              · ·/··/+-/-//++o++//:::::://///:--..·······       -                            ··/···+·..+.--:::////////////::--..·········        -                           ·+·····.··/··....···...---:---...··········          -                              ·.··/·+·.·············.···············            -                         ··     ····  ···························               -                      ···.·      /··      ·····················                 -                   ··.···      ·.·             ···········                      -                                                                                -                        Version X.X.X (frontend: xxx, engine: LambdaHack X.X.X) 
− GameDefinition/MoveKeys.txt
@@ -1,20 +0,0 @@-7 8 9        Walk throughout a level with the mouse or the numeric keypad- \|/         (top diagram) or the Vi editor keys (bottom) or with a compact-4-5-6        laptop setup (middle) that needs to be enabled in config.ui.ini.- /|\-1 2 3        Run until disturbed with Shift or Control. Go-to a position-             with LMB (left mouse button). Run collectively with S-LMB-             (press the button while holding Shift).-7 8 9- \|/         In aiming mode, the same keys (and mouse) move the x-hair-u-i-o        (aiming crosshair). Press `KP_5` (`5` on keypad) to wait,- /|\         bracing for impact, which reduces any damage taken-j k l        and prevents displacement by foes. Press `C-KP_5` or `C-KP_5`-             (the same key with Shift or Control) to lurk 0.1 of a turn,-             without bracing.-y k u- \|/         You displace enemies by running into them with Shift/Control-h-.-l        or S-LMB. Search, open, descend and attack by bumping into- /|\         walls, doors, stairs and enemies. The best item to attack with-b j n        is automatically chosen from among melee weapons in your-             personal equipment and your body parts.
GameDefinition/PLAYING.md view
@@ -1,44 +1,56 @@ Playing Allure of the Stars =========================== -The following blurb is a copy of the game intro screen.+The following backstory blurb is a copy of the game intro screen: -               Your party of trusty explorers is about to test-               its fortune by plundering a vast derelict-               passenger cruiser. The spaceship was abandoned-               and lost after an accident at the Solar System's-               outer frontier, but recently unexpectedly-               reappeared. Reportedly, deposit safes and jewelry-               inventories were never salvaged. Neither were-               emptied the gilded robot holds, the expensive-               arboretum nor the extravagant whole-deck habitat-               of natural and enhanced wild animals.+ Your party of trusty explorers is about+ to test its fortune by plundering a vast+ derelict passenger cruiser. The grand+ spaceship was abandoned and lost after+ an accident at the Solar System's outer+ frontier, but recently unexpectedly+ reappeared. Reportedly, deposit safes+ and jewelry inventories were never+ salvaged. Neither are emptied the gilded+ robot holds, the expensive arboretum+ nor the extravagant whole-deck habitat+ of natural and enhanced animals. -               Up to this day, countless autonomous life support-               and damage mitigation subsystems with redundant-               power sources busy themselves throughout dozens-               of decks. They seem successful, because there are-               abundant traces of ongoing metabolism all over-               the ship. That should make exploration so much-               easier.+ Up to this day, countless autonomous+ life support and damage mitigation+ subsystems with redundant power sources+ busy themselves throughout dozens+ of decks. They seem successful, because+ there are abundant traces of ongoing+ metabolism all over the ship. That+ should make exploration so much easier. -               Yours can't be the only shady Neptune Area crew-               interested in the giant wreck, so you shouldn't-               count on reaching it unopposed. The feral-               outer frontier denizens are not famed-               for scruples nor for restraint when using-               hazardous nano, cyber and bio technologies,-               so never assume you are safe. Be ready to hide-               in shadows, create distractions, set up ambushes,-               bump into unspeakable horrors, puzzling machinery-               and astounding treasures and make creative use-               of all you find, because you are on your own.-               If ever you turn your back in fear, expect to be-               chased tirelessly by sight, sound and smell.+ Yours can't be the only shady Neptune+ Area crew interested in the giant wreck,+ so you shouldn't count on reaching it+ unopposed. The feral outer frontier+ denizens are not famed for scruples+ nor for restraint when using hazardous+ nano, cyber and bio technologies,+ so never assume you are safe. Be ready+ to hide in shadows, create distractions,+ set up ambushes, bump into unspeakable+ horrors, puzzling machinery and+ astounding treasures. Gather clues,+ ideas and resources. Creatively use all+ you can find, because your survival+ depends on grit and ingenuity. Whenever+ your turn back in fear, expect to be+ chased via sight, sound and smell by+ those that don't fear and don't tire. ++What to expect+--------------+ Allure of the Stars is a turn-based game. You issue a command.-Then you watch its results unfold on the screen without you being able-to intervene. Then all settles down and you have as much time+Then you watch its results unfold on the screen, without you being able+to intervene. Then the dust settles and you have as much time as you want to inspect the battlefield and think about your next move.  Once the few basic command keys and on-screen symbols are learned,@@ -47,58 +59,191 @@ because the game's plot and characters are yet very lightly sketched. However there are already hours and days of replayable fun to be had and gameplay mastery to build, confirm via high scores and pit against-challenges. Contributions of all kinds are welcome.-Please offer feedback to mikolaj.konarski@funktory.com or, preferably,-at any of the public forums.+challenges, the first challenge being to finish all scenarios on standard+difficulty (5) that already requires careful planning and use of multiple+resources, but gives some leeway for trial, error and imperfect execution.+Role playing and piecing clues from text descriptions is mandatory+at the start of the longest scenario, but completely optional elsewhere+for players that prefer tactics and optimization challenges to storytelling. -If the game window is too large for your screen or you experience-other technical issues, please consult+The game differs from classic roguelikes in a few ways:++* player manually controls each member of his squad, though often the best+  tactics is to scout with only one character (a classic roguelike feel)+  and let others guard important areas++* the game is turn-based, but with visibly high granularity --- projectiles+  fly gradually over time with varying speeds and can be sidestepped+  or shot down; less so explosions that are swarms of projectile particles+  (turn-based just the same)++* time passes and factions pursue their goals on a few levels+  simultaneously, while other levels are frozen (but all are persistent)++* the same laws of simulated world apply to all factions and all actors,+  whether player-controlled or AI-controlled; e.g., the same field of view+  calculation, skill checks, equipment limitations, rules for item+  and terrain use++* combat mechanics is deterministic; randomness comes only from AI+  decisions and procedurally generated terrain++* there's (almost) no HP regeneration; attrition ensures all past (silly)+  decisions matter; HP of every actor starts at around half max++* each character has 10 uniform equipment slots, which fill quickly given+  that most melee weapons have cooldowns++* each faction has a single shared inventory of unlimited size,+  which has a physical location on the map and so can be ransacked++If the game window is too large for your screen or the game doesn't start+or you experience other technical issues, please consult [README.md](https://github.com/AllureOfTheStars/Allure/blob/master/README.md)-or describe your problem on gitter or the issue tracker.+or describe your problem on [Discord](https://discord.gg/87Ghnws)+or [Matrix](https://matrix.to/#/!HnbpAHMjOGHlYtrASl:mozilla.org)+or the issue tracker. Contributions of all kinds are welcome.+Please offer feedback to mikolaj.konarski@funktory.com or, preferably,+on any of the public forums.  -Game map---------+Starting your adventure+----------------------- -The map of any particular scenario consists of one or many levels-and each level has a large number of tiles with a particular+Commands for starting a new game, saving and exiting the current game,+configuring convenience settings and toggling AI control of the party+are listed in the main menu, brought up by the Esc key. Of the convenience+settings, the `suspect terrain` choice is of particular interest,+because it determines not only screen display of the level map,+but also whether suspect tiles are considered for mouse go-to, auto-explore+and for the `C-?` command that marks the nearest unexplored position.+Game difficulty, from the game setup menu, determines hitpoints at birth:+difficulty below 5 multiplies hitpoints of player characters, difficulty+over 5 multiplies hitpoints of their enemies. Each of the several named+optional challenges make the game much harder, but usually simpler, as well.++The "cold fish" challenge mode makes it impossible for player+characters to be healed by actors from other factions (this is+a significant restriction in the long crawl adventure).+The "ready goods" challenge mode disables crafting for the player,+making the selection of equipment, especially melee weapons, very limited,+unless the player has the luck to find the rare powerful ready weapons.+The "lone wolf" challenge mode reduces player's starting actors+to exactly one, though later on new heroes may join the party.+The "finder keeper" completely disables flinging projectiles+by the player, which affects not only ranged damage dealing,+but also throwing of consumables that buff teammates engaged in melee combat,+weaken and distract enemies, light dark corners, etc.++The game scenarios, as ordered by their number, lead the player along+an optional story arc. The first two adventures double as tutorials+that offer rudimentary preparation for the main game, the long crawl.+They gradually introduce exploration, stealth and melee combat,+helping the player develop his repertoire of squad formations+and move patterns, suitable for different tactical contexts.+When the player loses, a defeat message for the scenario appears+with hints about strategies known to work in the given tactical context.+Alternatively, the player may postpone reading these messages and instead+try to puzzle out the tactics himself --- this is not so hard, as there are+not so many moving parts to figure, at least in the short adventures.++As soon as the player learns to navigate initial levels of the crawl game+and starts employing ranged combat, light sources and other means+of gaining or denying battlefield intel, and dies a lot, it makes sense+to return to the remaining short adventures. They bring forth many extra+game features and tactics and prevent the player from missing half the fun+by trying to play the crawl just like a normal roguelike with spare heroes.+The extra scenarios continue the plotline from the initial tutorial adventures+in the form of flashbacks and eventually lead up to the events that start+the main crawl adventure. The training they provide has narrow focus,+drilling a particular skill set, even as exotic as opportunity fire+management, a frantic race to the exit and big asymmetric melee battle.+The challenge the scenarios offer may be, accordingly, quite extreme,+particularly at higher difficulty levels and when striving for high scores.++The main adventure, the long crawl, is the only one that takes place+over many floors, some initially blocked, requiring lots of time to beat.+The focus is on resource management and survival, including food gathering+and cooking, crafting, terrain transformation using tools, spotting+environment clues and guessing and countering opponents' strategies.+The player has a choice of exploring a single level at a time or portions+of many levels along a single staircase. On some levels he may explore+and loot with a single scout, eluding most opponents. On others he may be+forced to change the pace and perform a complete exterminatory sweep+involving his whole party. On yet others, his best course of action may be+to defend a key location until the first wave of attackers is broken.+The large game arena calls for strategic thinking, including resource+management, frantic races to the exit, big asymmetric melee battles.+Thus, the crawl scenario is the most replayable adventure, but even the small+ones can be refreshed by striving to beat a high score and then+ramping up the difficulty settings.+++Exploring the world+-------------------++The map of any particular adventure consists of one or many+levels and a level consists of a number of tiles with a particular terrain kind on each. The game world is persistent, i.e., every time-the player visits a level during a single game, its layout is the same.+the player visits a level during a single game, its layout is the same+(unless modified by other actors). -Terrain is depicted with non-letter and non-digit (except zero `0`)-characters, the same as items lying on the ground, though blocky-solid symbol are more likely to be non-passable terrain than items.-In case of doubt, one of the aiming commands (keypad `/`, with default-keybinding) cycles through all visible and remembered items on the level-and another (keypad `*`, with default keybinding) through all foes.-Also, pointing at a map position with `MMB` (middle mouse button) displays-a short description of its contents. The basic terrain kinds are as follows.+Letters and digits on the game screen are likely to represent actors.+On the other hand, terrain is depicted with non-letter and non-digit+characters and with zero `0`. Blocky solid symbols are likely to be+non-passable and/or not translucent terrain. White, cyan and green terrain+is usually inert, red is burning or trapped, blue activable or trapped,+magenta activable or searchable. -    terrain type                           on-screen symbol-    wall                                   #-    tree or rock or man-made column        0-    rubble                                 &-    bush, transparent obstacle             %-    trap, ice obstacle                     ^-    closed door                            +-    open door                              '-    smoke or fog                           ;-    ground, corridor                       .-    water                                  ~-    stairs or exit up                      <-    stairs or exit down                    >+Items lying on the ground are represented with non-letter and non-digit+characters, just as terrain, though rarely with blocky symbols.+In case of doubt, one of the aiming commands (`/` and `KP_/`,+that is, `/` on the keypad) cycles through all visible and remembered+items on the level and another (`*` and `KP_*`, all with default keybindings)+through all foes. Also, pointing at a map position with MMB+(middle mouse button) displays a short description of its contents. +Pointing with RMB enters aiming mode, in which pointing again+or pressing Space key or MMB decreases detail level of the description.+If a foe or interesting terrain is being pointed at, tilde key `~` shows+the relevant lore details, including crafting recipes afforded+by this terrain, if any. The basic terrain kinds are as follows.++    terrain type                  on-screen symbol++    bush, transparent obstacle    %+    trap, ice obstacle            ^++    wall                          #+    tree, rock, man-made column   0+    rubble                        &+    stairs, exit up               <+    stairs, exit down             >+    closed door                   +++    open door                     '+    ground, corridor              .+    water, other fluid            ~++    smoke, fog, open fire         ;+    workshop, curtain, foliage    :++The four groups above, in turn, block movement but not view,+block both, block neither, block view but not movement.+Additionally, each tile, regardless if open and if translucent,+may be permanently lit with ambient light or not.+ Actors are marked with lower and upper case letters and with characters `@` and `1` through `9` (but never `0`). Player-controlled-heroes are always bright white and by default they are selected-(e.g., to run together) so they have a blue highlight around their symbol.-If player manages to control animals or other actors, they retain their-letter and color, but gain a highlight as well.+heroes are always bright white and at game start they are selected+(e.g., to run together) so they have a green highlight around their symbol.+If player manages to take control of animals or other actors, they retain+their letter and color, but gain a highlight as well.  So, for example, the following map shows a room with a closed door, full of actors, connected by a corridor with a room with an open door, a pillar, a staircase down and rubble that obscures one of the corners.-The lower row of the larger room is full of items.+The lowest row of the larger room is full of items.      ######       ######     #@19.#########....&&@@ -107,273 +252,259 @@     ######       ##########  -Heroes-------+Leading your heroes+-------------------  The heroes are displayed on the map with bright white color (red if they are-about to fall down) and symbols `@` and `1` through `9` (never `0`).-The currently chosen party leader is yellow-highlighted on the map-and his attributes are displayed at the bottom-most status line which,+seriously wounded) and symbols `@` and `1` through `9` (never `0`).+The currently chosen party pointman is highlighted on the map with yellow.+The easiest way to control your team is to run a short distance+with your pointman using Shift-direction or LMB, switch the pointman+with the Tab key, repeat. In open terrain, if you keep consistent distance+between teammates, this resembles the leap-frog infantry tactics,+in which the immobile team members cover the movement of the others.+For best effects, try to end each sprint behind cover or concealment+(note that a thin pillar is neither, but a single shadowed spot may be+enough to hide in the dark).++Pointman hero's attributes are displayed at the bottom-most status line which, in its most complex form, looks as follows. -    *@12   2m/s Calm: 20/60 HP: 33/50 Leader: Haskell Alvin   6d1+5% 4d1+    *@12   2m/s Calm: 20/60 HP: 33/50 Pointman: Haskell Alvin   6d1+5% 4d1 -The line starts with the list of party members, with the current leader-highlighted in yellow. Most commands involve only the leader, including-movement with keyboard's keypad or `LMB` (left mouse button). If more-heroes are selected (highlighted in blue), they run together-whenever `:` or `S-LMB` (while holding Shift) over map area is pressed.-Any sleeping hero is highlighted in green and can be woken up-by yelling with `%`, which also taunts or stresses nearby enemies.+The line starts with the list of party members, with the current pointman+highlighted in yellow. Most commands involve only the pointman, including+movement with keyboard or keypad or LMB (left mouse button).+If more heroes are selected (highlighted in green), they run together+whenever `:` or S-LMB (LMB while holding down Shift) over map area+is pressed, though that's usually not a precise enough method+of controlling a team. Any sleeping hero is highlighted in blue+and can be woken up by yelling with `%` key, which also taunts+or stresses up nearby enemies. -Next on the bottom-most status line is the leader's current and maximum+Next on the bottom-most status line is the pointman's current and maximum Calm (morale, composure, focus, attentiveness), then his current and maximum HP (hit points, health). The colon after "Calm" turning-into a dot signifies that the leader is in a position without ambient-illumination, making a stealthy conduct easier. A brace sign instead-of a colon after "HP" means the leader is braced for combat-(see section [Basic Commands](#basic-commands)).+into a dot signifies that the pointman is in a position without ambient+illumination, making stealthy conduct easier. A brace sign instead+of a colon after "HP" means the pointman is braced for combat+(see section [Moving and acting](#Moving-and-acting)). -In the second half of the bottom-most status line, the leader's name-is shown. Then come damage dice of the leader's melee weapons and leader's+In the second half of the bottom-most status line, the pointman's name+is shown. Then come damage dice of the pointman's melee weapons and pointman's appendages, ordered by their power. The dice of the first recharged weapon,-the one that would be used in this moment, is adorned with percentage-damage bonus collected from the whole equipment of the leader.+the one that is going to be used now, is adorned with percentage+damage bonus collected from the whole equipment of the pointman. If the dice are displayed with upper-case `D` instead of lower-case `d`,-the weapon has additional effects apart of the usual kinetic damage.-The nature of the effects can be appraised via the `E`quipment screen.+the weapon has additional effects apart of the usual direct damage.+The nature of the effects can be appraised via the equipment outfit menu.+Only the most common piercing direct damage, denoted by the damage dice,+is affected by the percentage damage bonus. The other direct damage kinds,+such wounding and burning, are represented by extra added integers+and are not scaled by bonuses from melee skill nor maluses from+the opponent's armor.  Weapon damage and other item properties are displayed using the dice notation `xdy`, which denotes `x` rolls of `y`-sided dice. A variant written `xdLy` is additionally scaled by the level depth-in proportion to the maximal level depth (at the first level it's-always one, then it grows up to full rolled value at the last level).-Section [Monsters](#monsters) below describes combat resolution in detail,-including the role of the percentage damage bonus.+in proportion to the maximal level depth (at the first level the result+is always one, then it grows up to the full rolled value at the last level).+Section [Battling monsters](#Battling-monsters) below describes combat+resolution in detail, including the role of the percentage bonuses. -The second, the upper status line describes the current level in relation+The upper status line describes the current level in relation to the party. -    5  Lofty hall    [33% seen] X-hair: dire basilisk    [**__]+    5  Lofty hall    [33% seen] dire basilisk    [__**]  First comes the depth of the current level and its name. Then the percentage of its explorable tiles already seen by the heroes.-The `X-hair` (aiming crosshair) is the common focus of the whole party,+Then the common focus of the whole party, coming from the aiming crosshair marked on the map with a red box and manipulated with mouse or movement keys in aiming mode. In this example, the crosshair points-at a dire basilisk monster, with its hit points drawn as a bar.+at a dire basilisk monster with its hit points drawn as a half-full bar. -Instead of a monster, the `X-hair` area may describe a position on the map,-a recently spotted item on the floor or an item in inventory selected-for further action or, if none are available, a summary of the team status.-For example, this form+Instead of a monster, the aiming crosshair status area may describe+a position on the map, a recently spotted item on the floor or an item+in inventory selected for further action or, if none are available,+a summary of the team composition. For example, this form -    5  Lofty hall    [33% seen] X-hair: exact spot (71,12)    p15 l10+    5  Lofty hall    [33% seen] spot (71,12)    p15 l10  indicates that the party is aiming at an exact spot on the map.-At the end of the status line comes the length of the shortest-path from the leader's position to the spot and the straight-line-distance between the two points, one that a flung projectile would travel.+At the end of this example status line comes the length of the shortest+path from the pointman's position to the spot in crosshair and+the straight-line distance between the two points, one that a flung+projectile would travel if there were no obstacles.  -Basic Commands---------------+Moving and acting+----------------- -This section is a copy of the few basic screens of in-game help. The help-pages are automatically generated based on a game's keybinding content and-on overrides in the player's config file. The remaining in-game help screens,-not shown here, list all game commands grouped by categories in detail.-A text snapshot of the complete in-game help is in+This section is a copy of the few initial bits of in-game help. The in-game+help pages are automatically generated based on a game's keybinding content+definitions and on overrides in the player's config file. The remaining+in-game help screens, not shown here, list all game commands grouped+by categories in detail. A text snapshot of the complete in-game help is in [InGameHelp.txt](InGameHelp.txt). -Walk throughout a level with mouse or numeric keypad (left diagram below)-or the Vi editor keys (right) or with a compact laptop setup (middle) that-requires enabling in config.ui.ini. Run until disturbed with Shift or Control.-Go-to with LMB (left mouse button). Run collectively via S-LMB (holding Shift).+Walk throughout a level with mouse or numeric keypad (right diagram below)+or the Vi editor keys (middle) or the left-hand movement keys (left). Run until+disturbed with Shift or Control. Go-to a position with LMB (left mouse button). -               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+          q w e          y k u          7 8 9+           \|/            \|/            \|/+          a-s-d          h-.-l          4-5-6+           /|\            /|\            /|\+          z x c          b j n          1 2 3 -In aiming mode, the same keys (and mouse) move the x-hair (aiming crosshair).+In aiming mode, the same keys (and mouse) move the aiming crosshair. Press `KP_5` (`5` on keypad) to wait, bracing for impact, which reduces any damage taken and prevents displacement by foes. Press `S-KP_5` or `C-KP_5` (the same key with Shift or Control) to lurk 0.1 of a turn, without bracing.+ Displace enemies by running into them with Shift/Control or S-LMB. Search, open, descend and attack by bumping into walls, doors, stairs and enemies.-The best melee weapon is automatically chosen from your equipment-and from among your body parts.+The best, not on cooldown, melee weapon is automatically chosen from your+equipment and from among your body parts. -The following commands, joined with the basic set above,-let you accomplish anything in the game, though-not necessarily with the fewest keystrokes. You can also-play the game exclusively with a mouse, or both mouse-and keyboard. (See the ending help screens for mouse commands.)-Lastly, you can select a command with arrows or mouse directly-from the help screen or the dashboard and execute it on the spot.+The following few commands, joined with the movement and running keys,+let you accomplish almost anything in the game, though not necessarily+with the fewest keystrokes. You can also play the game exclusively+with a mouse, or both mouse and keyboard (e.g., mouse for go-to+and terrain inspection and keyboard for everything else). Lastly,+you can select a command with arrows or mouse directly from the help+screen or the dashboard and execute it on the spot.      keys         command-    E            manage equipment of the leader+    I            manage the shared inventory stash     g or ,       grab item(s)-    ESC          open main menu/finish aiming+    ESC          clear messages/open main menu/finish aiming     RET or INS   open dashboard/accept target-    SPACE        clear messages and show history-    S-TAB        cycle among all party members-    KP_* or !    cycle x-hair among enemies-    KP_/ or /    cycle x-hair among items-    c            close door-    %            yell/yawn+    SPACE        clear messages/show history/cycle detail level+    TAB          cycle among all party members+    *            cycle crosshair among enemies+    /            cycle crosshair among items+    M            modify any admissible terrain+    %            yell or yawn and stop sleeping -Screen area and UI mode (exploration/aiming) determine-mouse click effects. First, we give an overview-of effects of each button over the game map area.-The list includes not only left and right buttons, but also-the optional middle mouse button (MMB) and the mouse wheel,-which is also used over menus, to page-scroll them.-(For mice without RMB, one can use Control key with LMB and for mice-without MMB, one can use C-RMB or C-S-LMB.)+Screen area and UI mode (exploration/aiming) determine mouse click+effects. Here we give an overview of effects of each button over+the game map area. The list includes not only left and right buttons,+but also the optional middle mouse button (MMB) and the mouse wheel,+which is also used over menus, to page-scroll them. For mice without RMB,+one can use Control key with LMB and for mice without MMB, one can use+C-RMB or C-S-LMB. -    keys         command+    keys         command (exploration/aiming)     LMB          go to pointer for 25 steps/fling at enemy     S-LMB        run to pointer collectively for 25 steps/fling at enemy-    RMB or C-LMB start aiming at enemy under pointer-    S-RMB        open or close or alter at pointer-    MMB or C-RMB snap x-hair to floor under pointer+    RMB or C-LMB start aiming at enemy under pointer/cycle detail level+    S-RMB        modify terrain at pointer+    MMB or C-RMB snap crosshair to floor under pointer/cycle detail level     WHEEL-UP     swerve the aiming line     WHEEL-DN     unswerve the aiming line --Advanced Commands--------------------For ranged attacks, setting the aiming crosshair beforehand is not mandatory,-because x-hair is set automatically as soon as a monster comes into view-and can still be adjusted for as long as the missile to fling is not chosen.-However, sometimes you want to examine the level map tile by tile-or assign persistent personal targets to party members.-The latter is essential in the rare cases when your henchmen-(non-leader characters) can move autonomously or fire opportunistically-(via innate skills or rare equipment). Also, if your henchman is adjacent-to more than one enemy, setting his target makes him melee a particular foe.--You can enter the aiming mode with the `*` keypad key that selects-enemies or the `/` key that cycles among items on the floor-and marks a tile underneath an item. You can move x-hair-with direction keys and assign a personal target to the leader-with a `RET` key (Return, Enter). The details of the shared x-hair mark-are displayed in a status line close to the bottom of the screen,-as explained in section [Heroes](#heroes) above.--Commands for saving and exiting the current game, starting a new game,-configuring convenience settings for the current game and challenges-for the next game are listed in the main menu, brought up by the `ESC` key.-Game difficulty, from the challenges menu, determines hitpoints at birth:-difficulty below 5 multiplies hitpoints of player characters, difficulty-over 5 multiplies hitpoints of their enemies. Of the convenience settings,-the `suspect terrain` choice is particularly interesting, because it-determines not only screen display of the level map, but also whether-suspect tiles are considered for auto-explore and for the `C-?` command that-marks the nearest unexplored position.--The "lone wolf" challenge mode reduces player's starting actors to exactly-one (consequently, this does not affect the initial 'raid' scenario).-The "cold fish" challenge mode makes it impossible for player characters-to be healed by actors from other factions (this is a significant-restriction in the final 'crawl' scenario).--For a person new to roguelikes, the 'raid' scenario offers a gentle-introduction. The subsequent game scenarios lead the player along-an optional story arc. They gradually introduce squad combat,-stealth, opportunity fire, asymmetric battles and more.-Starting from the second scenario, the player controls a whole team-of characters and will develop his repertoire of squad formations,-preferred rendezvous locations and the use of light sources.--The last scenario, a multi-level crawl, is the gist and the main challenge-of the game. It spans 15 varied spaceship decks, requiring lots of time,-focus and flexibility to beat and providing considerable replayability.-The player has a choice of exploring a single level at a time or portions-of many floors along a single staircase. On some levels he may explore-and loot with a single scout eluding most opponents. On others he may be-forced to change the pace and perform a complete exterminatory sweep-involving his whole party. On yet others, his best course of action may be-to defend a key location until the first wave of attackers is broken.--The size of the crawl scenario calls for strategic thinking, including-resource management, area denial and unavoidable temporary retreats-to other levels. Compared to that, the smaller scenarios, where the arena-is a single floor only, provide a mostly tactical training-and additional entertainment by trying to beat a high-score.-They offer variety and a breather between the deaths^H^H^H^H^H^H the brave-attempts at the long crawl scenario. Some gameplay elements,-however, like totally symmetric skirmishes, a race to the exit or heavy-reliance on opportunity fire, feature only in the small scenarios.+Note that mouse is totally optional. Keyboard suffices, occasionally+requiring a lookup for some obscure command key in the help screens.  -Monsters---------+Battling monsters+----------------- -The life of the heroes is full of dangers. Monstrosities, natural+The life of heroes is full of danger. Monstrosities, natural and out of this world, roam the dark corridors 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.+and crowd and stampede all at once, sometimes brutally colliding by accident.  Monsters are depicted on the map with letters. Upper case letters-are unique monsters, often guardians of special floors, and lower case-letters are the rabble. If there are humans not from our team,-they are marked with `@` and `1` through `9` in other colours than white.+are unique monsters, often guardians of special floors, resources+and keys to other areas. Lower case letters are the rabble.+If there are humans not from our team, they are marked+with `@` and `1` through `9` in other colours than white.  When a hero walks and bumps into a monster or a monster attacks the hero, melee combat occurs. Hero *running* into and displacing-a monster (with the `Shift` or `Control` key), or the other way around,-does not inflict damage, but exchanges places. This gives the opponent+a monster (with the `Shift` key and, in case of keypad movement,+alternatively a `Control` key), does not involve inflicting a damage,+but only causes an exchange of places. This gives the opponent a free blow, but can improve the tactical situation or aid escape. In some circumstances actors are immune to the displacing, e.g., when both parties form a continuous front-line. -In melee combat, the best recharged equipped weapon (or the best fighting-organ that is not on cooldown) of each opponent is taken into account-for determining the damage and any extra effects of the blow.-To determine the damage dealt, the outcome of the weapon's damage dice roll-is multiplied by a percentage bonus. The bonus is calculated by taking-the damage bonus (summed from the equipped items of the attacker,-capped at 200%) minus the melee armor modifier of the defender-(capped at 200%, as well), with the outcome bounded between -99% and 99%,-which means that at least 1% of damage always gets through-and the damage is always lower than twice the dice roll.-The current leader's melee bonus, armor modifier and other detailed-skills can be viewed via the `#` command.+In melee combat, the best recharged equipped weapon (including fighting+organs that are not on cooldown) is taken into account for determining+the damage and any extra effects of the blow. To calculate the damage+dealt, the outcome of the weapon's direct piercing damage dice roll+(but not any additional direct damage summands such as wounding or burning)+is multiplied by a percentage bonus. The total bonus is calculated+by taking the damage bonus (summed from the equipped items and organs+and conditions of the attacker, capped at 200%) minus the melee+armor modifier of the defender (capped at 200%, as well). However,+at least 5% of damage always gets through, even if the bonus is nominally+below -95%, so excessively strong armor acts only as a buffer against+high melee skill of opponents. +The current pointman's melee bonus, armor modifier and other detailed+stats can be viewed in the skill menu, accessible via the `#` command,+which summarizes all the stats conferred by organs and conditions listed+in the organ menu, invoked by `@`.+ In ranged combat, the projectile is assumed to be attacking the defender in melee, using itself as the weapon, with the usual dice and damage bonus.-This time, the ranged armor skill of the defender is taken into account+This time, the *ranged* armor skill of the defender is taken into account and, additionally, the speed of the missile (based on shape and weight) figures in the calculation. You may propel any item from your inventory-(by default you are offered only the appropriate items; press `+` to cycle-item menu modes). Only items of a few kinds inflict any damage, but some+(by default you are offered only the appropriate items; press `+` to open+all choices). Only items of a few kinds inflict direct damage, but some have other effects, beneficial, detrimental or mixed.  In-game detailed item descriptions contain melee and ranged damage estimates.-They do not take into account damage from effects and, if bonuses are not-known, guesses are based on averages for the item kind in question.-The displayed figures are rounded, but the game internally keeps track-of minute fractions of HP.+They do not take into account enemy armor nor damage from effects and,+if bonuses are not known, guesses are based on averages for the item kind+in question. The displayed figures are rounded, but the game internally+keeps track of minute fractions of HP for all actors in all calculations. -The stress of combat drains Calm, gradually limiting viewing radius and,+The combat stress drains Calm, gradually limiting viewing radius and, if Calm reaches zero and the actor is sufficiently impressed by his foes,-making him defect and surrender to their domination.-Whenever the monster's or hero's hit points reach zero,-the combatant is incapacitated and promptly dies.-When the last hero dies or is dominated, the scenario ends in defeat.+making him defect and surrender unto their domination. Whenever the monster's+or hero's hit points reach zero, the combatant falls down and quickly+gets permanently incapacitated. When the last hero is disabled or dominated,+the adventure ends in defeat.  -On Winning and Dying---------------------+Attacking from a distance+------------------------- -You win a scenario if you escape the location alive (which may prove+For ranged attacks, setting the aiming crosshair before `f` to attack+is pressed is not mandatory. Crosshair is set automatically as soon+as a monster comes into view and can still be adjusted for as long+as the missile to fling is not chosen. However, sometimes before+flinging you want to examine the level map tile by tile by moving+the crosshair or assign persistent personal targets to party members.+The latter is essential in the rare cases when your henchmen+(non-pointman characters) can move autonomously or fire opportunistically+(via innate skills or rare equipment). Also, if your non-pointman character+is adjacent to more than one enemy, setting his target makes him melee+a particular foe.++You can enter the aiming mode with the `*` and `KP_*` keys that+select enemies or the `/` and `KP_/` keys that cycle among items+on the floor. You can move crosshair with direction keys and assign+a personal target to the pointman with a `RET` key (Return, Enter).+The details about the shared crosshair position are displayed in a status+line close to the bottom of the screen, as explained in section+[Leading your heroes](#Leading-your-heroes) above. You cycle aiming+mode from foe to spot to vector with the ``\`` key, which is useful+when a monster vanishes but you still want to fling at its last+known position.+++Winning and dying+-----------------++You win an adventure if you escape the location alive (which may prove difficult, because your foes tend to gradually build up the ambush squad blocking your escape route) or, in scenarios with no exit locations, if you eliminate all opposition. In the former case, your score@@ -382,10 +513,183 @@ of turns you spent overcoming your foes (the quicker the victory, the better; the slower the demise, the better). Bonus points, affected by the number of heroes lost, are awarded only if you win. The score is heavily-modified by the chosen game difficulty, but not by any other challenges.+modified by the chosen game difficulty, but not by any other challenges+(which are, however, proudly displayed in the high score listing). -When all your heroes fall, you are going to invariably see a new foolhardy-party of adventurers clamoring to be led into the unknown perils. They start-their conquest from a new entrance, with no experience and no equipment,-and new undaunted enemies bar their way. Lead the new hopeful explorers-to fame, wealth and glory!+When all your heroes fall, there's a hope they'd be found and brought+back to life using nanomedicine perfected for space travel hibernation.+The side effect of such a treatment is the loss of any bodily augmentations,+whether acquired by training, surviving harsh experiences or artificial+implantation. The heroes would start their next attempted conquest afresh,+with no endurance coming from experience, no supplies for survival+and no equipment. They should also expect new undaunted enemies+to bar their way.++The only immutable factor throughout the chain of the heroes' adventures+is their personal life stories, with the implied unique characteristics,+virtues and vices. However, a hero is not determined by the past+nor discouraged by present defeats and the allure of fame, wealth+and glory is always as bright as the stars.+++FAQ+---++- Q: I've already explored first three decks and I don't know what to do next.++A: Role-play a little. Re-read the scenario description, deck+descriptions for all three levels and the description of crucial+terrain items. Think in-character about visiting the lowest deck.+What would you do? What would you look for? Has anything changed+compared to what the texts describe? Would anything surprise you?+When you encounter any special terrain, actors, unexpected groups+of actors or items, ask questions, think how that ties to your initial+goal from the scenario description, examine terrain, actors and your+own items, discover and follow the plot, gather clues, find and set+for yourself new immediate goals, look for resources to attain them.+Don't be afraid to take notes. Eliminate hypotheses. Solve mysteries.+You don't need to move your heroes to examine positions on other+explored levels. It's enough to press `<` or `>` and move the crosshair+or use mouse. Feel free to recall the lore pertinent to a position+and use the lore menu by pressing `~`.++- Q: In the crawl scenario, I already investigated and decided+I won't use the `[spoiler]` nor `[spoiler]` way of escaping the ship.+`[Spoiler alert: players that didn't, may want to role-play+and try that first, see the previous question.]`+Assuming I want to attempt the way through the welded staircase,+how do I open the stairs up welded shut and reach the levels above+the first three?++A: Examine the staircase to determine how to open it. Look for clues+about who can have the right tool. If you didn't collect any clues+while investigating the other story elements, you need to bump+or examine interesting terrain more often. Listen to what encountered+unique characters say. Read the descriptions and piece the puzzles together.+If still no luck, it may be that there was a murder and the item+you look for got stranded. In that case, explore every tile,+pick up every item, equip it and try opening the staircase.+The item can't be destroyed and is too scary to be picked up by foes,+so your success is assured. Ask on a forum if desperate.+If that's any consolation, it's the only blocking puzzle in the game.+All other puzzles and mini-games can be brute-forced (with some losses).++- Q: Where to buy light sources in the crawl scenario game?++A: Too late to buy anything. However, they can be crafted in at least+two ways in the very early stages of the scenario. See the terrain lore menu+for crafting recipes after exploring the initial levels.++- Q: How do I craft?++A: Stand adjacent to the workshop terrain (blue colon on the map)+and experiment. Note that walkable positions can't be activated+by bumping and, anyway, crafting requires the Modify command (key `M`)+even for terrain that could be bumped into. Other crafting spots+(e.g., any water and fire) work similarly, but the recipes differ.+Read the recipes in the lore menu, terrain submenu, or by pressing `~`+when crosshair points at terrain that enables crafting.++- Q: Does power of crafted items depend on the depth of current level?++A: No, unlike items found on the floor or created via other means,+items created by crafting are considered to originate at maximal depth+whether regarding item kind, item stats or lifetime, if applicable.++- Q: Why do I summon hostile animals all the time, why do I defect+to the enemy faction every level, why am I constantly sabotaging+my own adventure, what is going on?++A: Whenever anything bad happens, notice it and use it as a learning+experience. Especially if it happens often or periodically.+Check carefully the messages overlaid on the map and in history log,+look at your outfit, organs, stats. Observe coincidences.+Build conjectures. Deduce. Prevent. Adjust. Win. Whomever told you+bumping is enough, lied.++- Q: Why is my hero immobile?++A: Perhaps he's just sleeping (blue box indicates that)? If so,+you can wake him up with the `%` command. If he's not asleep,+his movement skill may be temporarily drained. Switch to another hero+or perform some other productive action that walking or wait+or rest with `R`.++- Q: Is autoexplore safe?++A: Not at all. It doesn't try to guess which hazardous terrain you want+to avoid and which to barge through, so be prepared to abort exploration+if open fire or slippery ground comes into view. Unless you have HP to spare.++- Q: Why does the percentage of explored tiles turn from 100% to 99%?++A: Apparently enemies transformed a tile from unexplorable terrain kind+to explorable. The new tile has never been seen by the player,+so the percentage is no longer at 100%.++- Q: Why when a single hero gets ambushed and is fighting at close quarters,+his distant teammates don't jointly come to his rescue.++A: The teammates wait for him to come back into the formation instead+so that they may assume a front line and then melee their foes together.+The immobile heroes are assumed to be pinned to their positions+by fear and shock, but also by their imperative to hold formation,+so as to defend an important position or avoid running piecemeal+into a trap or into friendly fire or avoid breaking concealment+and revealing their position or leaving a vantage point from which+they can observe and relay enemy movement. For untrained teams,+simultaneous synchronized squad movement is not feasible.+It would be practical, if all squaddies had cameras, with a few drones+overhead for best effect, and if a team of off-site coordinators analyzed+the situation and micromanaged them all. This is not the case here.++- Q: Why is the noise I'm hearing "indistinct"?++A: That's because it's out of direct hearing range of each teammate+on the level, but ponderous enough to be perceived by all as vibrations+and echos from afar. Any other noise adjective indicates that the noise+is heard by at least one teammate and how far it is from the pointman+(who may or may not hear it directly, as signalled by his Calm drop).++- Q: Why are there two 'weakened' conditions in the organ menu of my hero?++A: Each team has a different recipe for their weakness brew.+Consequently, multiple affliction by the concoction from a single team+prolongs a single malady, but affliction by concoctions from many teams+causes concurrent ailments, with compounded effects, but independent+and short durations. The benefit of the mechanism is that+it's possible to tell the perpetrator team of any ailment.+The exceptions are the conditions that activate each turn, e.g., healing+(regeneration, various resistances that effectively cure each turn)+or wounding (poison). These are similar regardless of the team and so+the condition is always only prolonged.++- Q: Why is a harpoon in my shared inventory stash charging for hundreds+of turns?++A: This is an artifact of time running independently on each level.+Any ideas on how to improve this are welcome. A workaround is to drop and then+pick up the item on the level you want to use it. When picked up,+it gets recharged after, randomly, from one to two times the normal+cooldown period of the item and then recharges normally for as long+as it's used on this level.++- Q: Why the bottom line displays a weapon with a timeout to the right+of a weapon without timeout? Doesn't it mean the former is never used?++A: Yes, it's never used and quite possibly it's your mistake and if not,+at least a very special situation and the display reminds you about it.+Shuffle the equipment among your team if you want the weapon+to get used.++- Q: When can I learn all characters' backstory items?++A: Each backstory item triggers in specific circumstances, so it may take+some time and even, rarely, never be revealed at all. For vices, that may be+the desired turn of action. Alternatively, you can try to reveal a vice+early and then prevent it from triggering ever again once the activation+condition is known.++- Q: Can I kill off heroes permanently and reset all character backstories?++A: Yes (not judging). Just remove all savefiles.
GameDefinition/config.ui.default view
@@ -1,5 +1,5 @@ ; Copyright (c) 2008--2011 Andres Loeh-; Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+; Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) ; This file is a part of the computer game Allure of the Stars ; and is released under the terms of the GNU Affero General Public License. ; For license and copyright information, see the file LICENSE.@@ -20,39 +20,116 @@  [additional_commands] ; Angband compatibility (accept target)-Cmd_2 = ("KP_Insert", ([CmdAim], "", ByAimMode AimModeCmd {exploration = ExecuteIfClear Dashboard, aiming = Accept}))+Cmd_2 = ("KP_Insert", ([CmdAim], "", ByAimMode AimModeCmd {exploration = Dashboard, aiming = Accept}))+; Custom key rebinding example:+;+;   Cmd_3 = ("a", ([CmdItem], "My Happy Cmd", Macro ["t"]))+;+; Explanation: to (t)rigger a consumable item for use, you normally use+;              the key press 't'.+; Suppose you would like to use the key press 'a' instead of 't'.+;   Cmd_3          - can be anything as long as it doesn't conflict or+;                    overlap with a binding already defined+;   "a"            - the new key to use+;   [CmdItem]      - can either be [] or [CmdItem]. If using [CmdItem]+;                    the new keybinding will be displayed in the help menu+;                    in-game alongside other keys related to handling items.+;                    If there is no need to have the new binding displayed+;                    in the help menu, it is recommended to leave this empty []+;   "My Happy Cmd" - Is a name given to your binding, which would be dispayed+;                    in the help page in-game if you use 'CmdItem' from above  [hero_names] HeroName_0 = ("Haskell Alvin", "he") HeroName_1 = ("Alonzo Barkley", "he")-HeroName_2 = ("Ines Galenti", "she")+HeroName_2 = ("Inés Galenti", "she") HeroName_3 = ("Ernst Abraham", "he") HeroName_4 = ("Samuel Saunders", "he") HeroName_5 = ("Roger Robin", "he") HeroName_6 = ("Christopher Flatt", "he")+HeroName_7 = ("Simon Wise", "he")+HeroName_8 = ("Daniel Homer", "he")+HeroName_9 = ("Oleg Cracker", "he")  [ui]-; These two are mutually exclusive:+; Disable these to free up some keys, if you want to rebind commands+; and/or to avoid moving due to accidental key presses. movementViKeys_hjklyubn = True-movementLaptopKeys_uk8o79jl = False-; The font to use for scaling SDL2 display (best by multiples or 0.5):-;sdlFontFile = "16x16xw.woff"-scalableFontSize = 16-sdlScalableSizeAdd = 0-sdlFontFile = "16x16xw.bdf"-sdlBitmapSizeAdd = 0-;sdlFontFile = "8x8xb.fnt"-;sdlFontFile = "8x8x.fnt"-;sdlBitmapSizeAdd = 2+movementLeftHandKeys_axwdqezc = True+; recommended: "binary" or "dejavuBold" or, if not a Debian package, "ubuntu"+chosenFontset = "binary"+; 1.0 means don't scale; 1.5, 2.0, 3.0 give good results, <0.7 very bad+; for scales < 1 try fontsets with bolder auxiliary fonts and/or HintingLight+allFontsScale = 1.0+; NotFullscreen (default), BigBorderlessWindow (preferred), ModeChange+; for 1920×1080 fullscreen, set allFontsScale = 1.5 above+fullscreenMode = NotFullscreen+screenOneMessagePerLine = True+historyOneMessagePerLine = True ; New historyMax takes effect after removal of savefiles.-historyMax = 5000+; Looks best if is divisble by screenful of message (rheight - 4 = 41).+historyMax = 5002 maxFps = 24 noAnim = False hpWarningPercent = 20-; Uncomment to make all messages white.-; messageColors = [] overrideCmdline = ""-; Legacy: monospace fonts that have fixed size regardless of boldness (on some OSes at least)-gtkFontFamily = "DejaVu Sans Mono,Consolas,Courier New,Liberation Mono,Courier,FreeMono,Monospace"-; sdlFonSizeAdd is now ignored-; runStopMsgs is now ignored++[fonts]+; the auxiliary fonts (the format is: kind name size hinting):+binaryRegular = FontProportional "BinarySansProLH-Regular.ttf.woff" 16 HintingHeavy+binaryBold = FontProportional "BinarySansProLH-Semibold.ttf.woff" 16 HintingHeavy+binaryMono = FontMonospace "BinaryCodeProLH-Bold.ttf.woff" 14 HintingHeavy+dejavuRegular = FontProportional "DejaVuLGCSans.ttf.woff" 15 HintingHeavy+dejavuBold = FontProportional "DejaVuLGCSans-Bold.ttf.woff" 13 HintingHeavy+dejavuMono = FontMonospace "Hack-Bold.ttf.woff" 13 HintingHeavy+ubuntuRegular = FontProportional "ubuntu-v14-latin-ext_latin-regular.ttf.woff" 16 HintingLight+ubuntuBold = FontProportional "ubuntu-v14-latin-ext_latin-500.ttf.woff" 16 HintingLight+ubuntuMono = FontMonospace "ubuntu-mono-v9-latin-ext_latin-700.ttf.woff" 16 HintingLight+; the map fonts (the format is: kind name size hinting cellSizeAdd):+; with allFontsScale < 1, try HintingLight for fuzzy, but less distorted shapes:+16x16xwScalable = FontMapScalable "16x16xw.woff" 16 HintingHeavy 0+; the map fonts (the format is: kind name cellSizeAdd):+16x16xwBitmap = FontMapBitmap "16x16xw.bdf" 0+8x8xbBitmap = FontMapBitmap "8x8xb.fnt" 2+8x8xBitmap = FontMapBitmap "8x8x.fnt" 2++[fontsets]+; best proportional:+binary = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "binaryRegular", fontPropBold = "binaryBold", fontMono = "binaryMono"}+dejavuBold = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "dejavuBold", fontPropBold = "dejavuBold", fontMono = "dejavuMono"}++; Warning: the Ubuntu Font Family fonts are considered non-free by Debian+; and so we don't include them in Debian and some other GNU/Linux packages.+; If you use such a package, feel free to download the fonts from our github+; repository or specify absolute paths to their locations in non-free Debian+; package fonts-ubuntu.+ubuntu = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "ubuntuRegular", fontPropBold = "ubuntuBold", fontMono = "ubuntuMono"}++; decent proportional:+binaryBold = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "binaryBold", fontPropBold = "binaryBold", fontMono = "binaryMono"}+dejavu = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "dejavuRegular", fontPropBold = "dejavuBold", fontMono = "dejavuMono"}+ubuntuThin = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "ubuntuRegular", fontPropBold = "ubuntuRegular", fontMono = "ubuntuMono"}++; monospace, for people that don't like proportional or many fonts:+binaryMono = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "binaryMono", fontPropBold = "binaryMono", fontMono = "binaryMono"}+dejavuMono = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "dejavuMono", fontPropBold = "dejavuMono", fontMono = "dejavuMono"}+ubuntuMono = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "ubuntuMono", fontPropBold = "ubuntuMono", fontMono = "ubuntuMono"}++; square:+16x16xw = FontSet {fontMapScalable = "16x16xwScalable", fontMapBitmap = "16x16xwBitmap", fontPropRegular = "", fontPropBold = "", fontMono = ""}+8x8xb = FontSet {fontMapScalable = "", fontMapBitmap = "8x8xbBitmap", fontPropRegular = "", fontPropBold = "", fontMono = ""}+8x8x = FontSet {fontMapScalable = "", fontMapBitmap = "8x8xBitmap", fontPropRegular = "", fontPropBold = "", fontMono = ""}++[message_colors]+; Prefixes of message class constructor names paired with colors.+; The first prefix that matches, wins.+;+; E.g., uncomment to make all messages white:+; Msg = White++[version]+; If an outdated config file mentions fonts that are not bundled with the game+; any more, the game crashes. To prevent that, one of the first three components+; of the game version should be bumped whenever fonts are changed.+; Configs from old versions are rejected, preventing the crash.+version = 0.10.2
+ GameDefinition/fonts/BinaryCodeProLH-Bold.ttf.woff view

binary file changed (absent → 70820 bytes)

+ GameDefinition/fonts/BinarySansProLH-Regular.ttf.woff view

binary file changed (absent → 75896 bytes)

+ GameDefinition/fonts/BinarySansProLH-Semibold.ttf.woff view

binary file changed (absent → 75964 bytes)

+ GameDefinition/fonts/DejaVuLGCSans-Bold.ttf.woff view

binary file changed (absent → 230292 bytes)

+ GameDefinition/fonts/DejaVuLGCSans.ttf.woff view

binary file changed (absent → 244944 bytes)

− GameDefinition/fonts/Fix15Mono-Bold.woff

binary file changed (101756 → absent bytes)

+ GameDefinition/fonts/Hack-Bold.ttf.woff view

binary file changed (absent → 134196 bytes)

− GameDefinition/fonts/LICENSE.16x16x
@@ -1,339 +0,0 @@-                    GNU GENERAL PUBLIC LICENSE-                       Version 2, June 1991-- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA- Everyone is permitted to copy and distribute verbatim copies- of this license document, but changing it is not allowed.--                            Preamble--  The licenses for most software are designed to take away your-freedom to share and change it.  By contrast, the GNU General Public-License is intended to guarantee your freedom to share and change free-software--to make sure the software is free for all its users.  This-General Public License applies to most of the Free Software-Foundation's software and to any other program whose authors commit to-using it.  (Some other Free Software Foundation software is covered by-the GNU Lesser General Public License instead.)  You can apply it to-your programs, too.--  When we speak of free software, we are referring to freedom, not-price.  Our General Public Licenses are designed to make sure that you-have the freedom to distribute copies of free software (and charge for-this service if you wish), that you receive source code or can get it-if you want it, that you can change the software or use pieces of it-in new free programs; and that you know you can do these things.--  To protect your rights, we need to make restrictions that forbid-anyone to deny you these rights or to ask you to surrender the rights.-These restrictions translate to certain responsibilities for you if you-distribute copies of the software, or if you modify it.--  For example, if you distribute copies of such a program, whether-gratis or for a fee, you must give the recipients all the rights that-you have.  You must make sure that they, too, receive or can get the-source code.  And you must show them these terms so they know their-rights.--  We protect your rights with two steps: (1) copyright the software, and-(2) offer you this license which gives you legal permission to copy,-distribute and/or modify the software.--  Also, for each author's protection and ours, we want to make certain-that everyone understands that there is no warranty for this free-software.  If the software is modified by someone else and passed on, we-want its recipients to know that what they have is not the original, so-that any problems introduced by others will not reflect on the original-authors' reputations.--  Finally, any free program is threatened constantly by software-patents.  We wish to avoid the danger that redistributors of a free-program will individually obtain patent licenses, in effect making the-program proprietary.  To prevent this, we have made it clear that any-patent must be licensed for everyone's free use or not licensed at all.--  The precise terms and conditions for copying, distribution and-modification follow.--                    GNU GENERAL PUBLIC LICENSE-   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION--  0. This License applies to any program or other work which contains-a notice placed by the copyright holder saying it may be distributed-under the terms of this General Public License.  The "Program", below,-refers to any such program or work, and a "work based on the Program"-means either the Program or any derivative work under copyright law:-that is to say, a work containing the Program or a portion of it,-either verbatim or with modifications and/or translated into another-language.  (Hereinafter, translation is included without limitation in-the term "modification".)  Each licensee is addressed as "you".--Activities other than copying, distribution and modification are not-covered by this License; they are outside its scope.  The act of-running the Program is not restricted, and the output from the Program-is covered only if its contents constitute a work based on the-Program (independent of having been made by running the Program).-Whether that is true depends on what the Program does.--  1. You may copy and distribute verbatim copies of the Program's-source code as you receive it, in any medium, provided that you-conspicuously and appropriately publish on each copy an appropriate-copyright notice and disclaimer of warranty; keep intact all the-notices that refer to this License and to the absence of any warranty;-and give any other recipients of the Program a copy of this License-along with the Program.--You may charge a fee for the physical act of transferring a copy, and-you may at your option offer warranty protection in exchange for a fee.--  2. You may modify your copy or copies of the Program or any portion-of it, thus forming a work based on the Program, and copy and-distribute such modifications or work under the terms of Section 1-above, provided that you also meet all of these conditions:--    a) You must cause the modified files to carry prominent notices-    stating that you changed the files and the date of any change.--    b) You must cause any work that you distribute or publish, that in-    whole or in part contains or is derived from the Program or any-    part thereof, to be licensed as a whole at no charge to all third-    parties under the terms of this License.--    c) If the modified program normally reads commands interactively-    when run, you must cause it, when started running for such-    interactive use in the most ordinary way, to print or display an-    announcement including an appropriate copyright notice and a-    notice that there is no warranty (or else, saying that you provide-    a warranty) and that users may redistribute the program under-    these conditions, and telling the user how to view a copy of this-    License.  (Exception: if the Program itself is interactive but-    does not normally print such an announcement, your work based on-    the Program is not required to print an announcement.)--These requirements apply to the modified work as a whole.  If-identifiable sections of that work are not derived from the Program,-and can be reasonably considered independent and separate works in-themselves, then this License, and its terms, do not apply to those-sections when you distribute them as separate works.  But when you-distribute the same sections as part of a whole which is a work based-on the Program, the distribution of the whole must be on the terms of-this License, whose permissions for other licensees extend to the-entire whole, and thus to each and every part regardless of who wrote it.--Thus, it is not the intent of this section to claim rights or contest-your rights to work written entirely by you; rather, the intent is to-exercise the right to control the distribution of derivative or-collective works based on the Program.--In addition, mere aggregation of another work not based on the Program-with the Program (or with a work based on the Program) on a volume of-a storage or distribution medium does not bring the other work under-the scope of this License.--  3. You may copy and distribute the Program (or a work based on it,-under Section 2) in object code or executable form under the terms of-Sections 1 and 2 above provided that you also do one of the following:--    a) Accompany it with the complete corresponding machine-readable-    source code, which must be distributed under the terms of Sections-    1 and 2 above on a medium customarily used for software interchange; or,--    b) Accompany it with a written offer, valid for at least three-    years, to give any third party, for a charge no more than your-    cost of physically performing source distribution, a complete-    machine-readable copy of the corresponding source code, to be-    distributed under the terms of Sections 1 and 2 above on a medium-    customarily used for software interchange; or,--    c) Accompany it with the information you received as to the offer-    to distribute corresponding source code.  (This alternative is-    allowed only for noncommercial distribution and only if you-    received the program in object code or executable form with such-    an offer, in accord with Subsection b above.)--The source code for a work means the preferred form of the work for-making modifications to it.  For an executable work, complete source-code means all the source code for all modules it contains, plus any-associated interface definition files, plus the scripts used to-control compilation and installation of the executable.  However, as a-special exception, the source code distributed need not include-anything that is normally distributed (in either source or binary-form) with the major components (compiler, kernel, and so on) of the-operating system on which the executable runs, unless that component-itself accompanies the executable.--If distribution of executable or object code is made by offering-access to copy from a designated place, then offering equivalent-access to copy the source code from the same place counts as-distribution of the source code, even though third parties are not-compelled to copy the source along with the object code.--  4. You may not copy, modify, sublicense, or distribute the Program-except as expressly provided under this License.  Any attempt-otherwise to copy, modify, sublicense or distribute the Program is-void, and will automatically terminate your rights under this License.-However, parties who have received copies, or rights, from you under-this License will not have their licenses terminated so long as such-parties remain in full compliance.--  5. You are not required to accept this License, since you have not-signed it.  However, nothing else grants you permission to modify or-distribute the Program or its derivative works.  These actions are-prohibited by law if you do not accept this License.  Therefore, by-modifying or distributing the Program (or any work based on the-Program), you indicate your acceptance of this License to do so, and-all its terms and conditions for copying, distributing or modifying-the Program or works based on it.--  6. Each time you redistribute the Program (or any work based on the-Program), the recipient automatically receives a license from the-original licensor to copy, distribute or modify the Program subject to-these terms and conditions.  You may not impose any further-restrictions on the recipients' exercise of the rights granted herein.-You are not responsible for enforcing compliance by third parties to-this License.--  7. If, as a consequence of a court judgment or allegation of patent-infringement or for any other reason (not limited to patent issues),-conditions are imposed on you (whether by court order, agreement or-otherwise) that contradict the conditions of this License, they do not-excuse you from the conditions of this License.  If you cannot-distribute so as to satisfy simultaneously your obligations under this-License and any other pertinent obligations, then as a consequence you-may not distribute the Program at all.  For example, if a patent-license would not permit royalty-free redistribution of the Program by-all those who receive copies directly or indirectly through you, then-the only way you could satisfy both it and this License would be to-refrain entirely from distribution of the Program.--If any portion of this section is held invalid or unenforceable under-any particular circumstance, the balance of the section is intended to-apply and the section as a whole is intended to apply in other-circumstances.--It is not the purpose of this section to induce you to infringe any-patents or other property right claims or to contest validity of any-such claims; this section has the sole purpose of protecting the-integrity of the free software distribution system, which is-implemented by public license practices.  Many people have made-generous contributions to the wide range of software distributed-through that system in reliance on consistent application of that-system; it is up to the author/donor to decide if he or she is willing-to distribute software through any other system and a licensee cannot-impose that choice.--This section is intended to make thoroughly clear what is believed to-be a consequence of the rest of this License.--  8. If the distribution and/or use of the Program is restricted in-certain countries either by patents or by copyrighted interfaces, the-original copyright holder who places the Program under this License-may add an explicit geographical distribution limitation excluding-those countries, so that distribution is permitted only in or among-countries not thus excluded.  In such case, this License incorporates-the limitation as if written in the body of this License.--  9. The Free Software Foundation may publish revised and/or new versions-of the General Public License from time to time.  Such new versions will-be similar in spirit to the present version, but may differ in detail to-address new problems or concerns.--Each version is given a distinguishing version number.  If the Program-specifies a version number of this License which applies to it and "any-later version", you have the option of following the terms and conditions-either of that version or of any later version published by the Free-Software Foundation.  If the Program does not specify a version number of-this License, you may choose any version ever published by the Free Software-Foundation.--  10. If you wish to incorporate parts of the Program into other free-programs whose distribution conditions are different, write to the author-to ask for permission.  For software which is copyrighted by the Free-Software Foundation, write to the Free Software Foundation; we sometimes-make exceptions for this.  Our decision will be guided by the two goals-of preserving the free status of all derivatives of our free software and-of promoting the sharing and reuse of software generally.--                            NO WARRANTY--  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,-REPAIR OR CORRECTION.--  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE-POSSIBILITY OF SUCH DAMAGES.--                     END OF TERMS AND CONDITIONS--            How to Apply These Terms to Your New Programs--  If you develop a new program, and you want it to be of the greatest-possible use to the public, the best way to achieve this is to make it-free software which everyone can redistribute and change under these terms.--  To do so, attach the following notices to the program.  It is safest-to attach them to the start of each source file to most effectively-convey the exclusion of warranty; and each file should have at least-the "copyright" line and a pointer to where the full notice is found.--    <one line to give the program's name and a brief idea of what it does.>-    Copyright (C) <year>  <name of author>--    This program is free software; you can redistribute it and/or modify-    it under the terms of the GNU General Public License as published by-    the Free Software Foundation; either version 2 of the License, or-    (at your option) any later version.--    This program is distributed in the hope that it will be useful,-    but WITHOUT ANY WARRANTY; without even the implied warranty of-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-    GNU General Public License for more details.--    You should have received a copy of the GNU General Public License along-    with this program; if not, write to the Free Software Foundation, Inc.,-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.--Also add information on how to contact you by electronic and paper mail.--If the program is interactive, make it output a short notice like this-when it starts in an interactive mode:--    Gnomovision version 69, Copyright (C) year name of author-    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.-    This is free software, and you are welcome to redistribute it-    under certain conditions; type `show c' for details.--The hypothetical commands `show w' and `show c' should show the appropriate-parts of the General Public License.  Of course, the commands you use may-be called something other than `show w' and `show c'; they could even be-mouse-clicks or menu items--whatever suits your program.--You should also get your employer (if you work as a programmer) or your-school, if any, to sign a "copyright disclaimer" for the program, if-necessary.  Here is a sample; alter the names:--  Yoyodyne, Inc., hereby disclaims all copyright interest in the program-  `Gnomovision' (which makes passes at compilers) written by James Hacker.--  <signature of Ty Coon>, 1 April 1989-  Ty Coon, President of Vice--This General Public License does not permit incorporating your program into-proprietary programs.  If your program is a subroutine library, you may-consider it more useful to permit linking proprietary applications with the-library.  If this is what you want to do, use the GNU Lesser General-Public License instead of this License.
− GameDefinition/fonts/LICENSE.Fix15Mono-Bold
@@ -1,94 +0,0 @@-Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
-with Reserved Font Name < Fira >, 
-
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at:
-http://scripts.sil.org/OFL
-
-
------------------------------------------------------------
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
------------------------------------------------------------
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide
-development of collaborative font projects, to support the font creation
-efforts of academic and linguistic communities, and to provide a free and
-open framework in which fonts may be shared and improved in partnership
-with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and
-redistributed freely as long as they are not sold by themselves. The
-fonts, including any derivative works, can be bundled, embedded, 
-redistributed and/or sold with any software provided that any reserved
-names are not used by derivative works. The fonts and derivatives,
-however, cannot be released under any other type of license. The
-requirement for fonts to remain under this license does not apply
-to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright
-Holder(s) under this license and clearly marked as such. This may
-include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the
-copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as
-distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting,
-or substituting -- in part or in whole -- any of the components of the
-Original Version, by changing formats or by porting the Font Software to a
-new environment.
-
-"Author" refers to any designer, engineer, programmer, technical
-writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the Font Software, to use, study, copy, merge, embed, modify,
-redistribute, and sell modified and unmodified copies of the Font
-Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components,
-in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled,
-redistributed and/or sold with any software, provided that each copy
-contains the above copyright notice and this license. These can be
-included either as stand-alone text files, human-readable headers or
-in the appropriate machine-readable metadata fields within text or
-binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font
-Name(s) unless explicit written permission is granted by the corresponding
-Copyright Holder. This restriction only applies to the primary font name as
-presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
-Software shall not be used to promote, endorse or advertise any
-Modified Version, except to acknowledge the contribution(s) of the
-Copyright Holder(s) and the Author(s) or with their explicit written
-permission.
-
-5) The Font Software, modified or unmodified, in part or in whole,
-must be distributed entirely under this license, and must not be
-distributed under any other license. The requirement for fonts to
-remain under this license does not apply to any document created
-using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are
-not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
+ GameDefinition/fonts/ubuntu-mono-v9-latin-ext_latin-700.ttf.woff view

binary file changed (absent → 57416 bytes)

+ GameDefinition/fonts/ubuntu-v14-latin-ext_latin-500.ttf.woff view

binary file changed (absent → 69288 bytes)

+ GameDefinition/fonts/ubuntu-v14-latin-ext_latin-regular.ttf.woff view

binary file changed (absent → 76672 bytes)

GameDefinition/game-src/Client/UI/Content/Input.hs view
@@ -1,5 +1,5 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE.@@ -10,7 +10,7 @@   ( standardKeysAndMouse #ifdef EXPOSE_INTERNAL     -- * Internal operations-  , closeDoorTriggers, applyTs+  , applyTs #endif   ) where @@ -18,10 +18,9 @@  import Game.LambdaHack.Core.Prelude -import           Game.LambdaHack.Client.UI.Content.Input-import           Game.LambdaHack.Client.UI.HumanCmd-import qualified Game.LambdaHack.Content.TileKind as TK-import           Game.LambdaHack.Definition.Defs+import Game.LambdaHack.Client.UI.Content.Input+import Game.LambdaHack.Client.UI.HumanCmd+import Game.LambdaHack.Definition.Defs  -- | Description of default key-command bindings. --@@ -37,115 +36,85 @@   -- mode) first.    -- Main menu-  [ ("e", ([CmdMainMenu], "enter challenges menu>", ChallengesMenu))-  , ("s", ([CmdMainMenu], "start new game", GameRestart))+  [ ("s", ([CmdMainMenu], "setup and start new game>", ChallengeMenu))   , ("x", ([CmdMainMenu], "save and exit to desktop", GameExit))-  , ("v", ([CmdMainMenu], "visit settings menu>", SettingsMenu))-  , ("t", ([CmdMainMenu], "toggle autoplay (insert coin)", AutomateToggle))+  , ("v", ([CmdMainMenu], "tweak convenience settings>", SettingsMenu))+  , ("t", ([CmdMainMenu], "toggle autoplay", AutomateToggle))   , ("?", ([CmdMainMenu], "see command help", Help))   , ("F12", ([CmdMainMenu], "switch to dashboard", Dashboard))   , ("Escape", ([CmdMainMenu], "back to playing", AutomateBack))    -- Minimal command set, in the desired presentation order.   -- A lot of these are not necessary, but may be familiar to new players.-  , ("E", ( [CmdMinimal, CmdItem, CmdDashboard]-          , "manage equipment of the leader"+  -- Also a few non-minimal item commands to keep proper order.+  , ("I", ( [CmdMinimal, CmdItem, CmdDashboard]+          , "manage the shared inventory stash"+          , ChooseItemMenu (MStore CStash) ))+  , ("O", ( [CmdItem, CmdDashboard]+          , "manage the equipment outfit of the pointman"           , ChooseItemMenu (MStore CEqp) ))   , ("g", addCmdCategory CmdMinimal $ grabItems "grab item(s)")   , ("Escape", ( [CmdMinimal, CmdAim]-               , "open main menu/finish aiming"-               , ByAimMode AimModeCmd { exploration =-                                          ExecuteIfClear MainMenuAutoOff-                                      , aiming = Cancel } ))-  , ("C-Escape", ([CmdNoHelp], "", MainMenuAutoOn))+               , "clear messages/open main menu/finish aiming"+               , ByAimMode AimModeCmd+                             { exploration = ExecuteIfClear MainMenuAutoOff+                             , aiming = Cancel } ))+  , ("C-Escape", ([], "", MainMenuAutoOn))       -- required by frontends; not shown   , ("Return", ( [CmdMinimal, CmdAim]                , "open dashboard/accept target"-               , ByAimMode AimModeCmd { exploration = ExecuteIfClear Dashboard+               , ByAimMode AimModeCmd { exploration = Dashboard                                       , aiming = Accept } ))-  , ("space", ( [CmdMinimal, CmdMeta]-              , "clear messages and show history"-              , ExecuteIfClear LastHistory ))-  , ("Tab", ( [CmdMove]-            , "cycle among party members on the level"-            , MemberCycle ))-      -- listed here to keep proper order-  , ("BackTab", ( [CmdMinimal, CmdMove]-              , "cycle among all party members"-              , MemberBack ))-  , ("KP_Multiply", ( [CmdMinimal, CmdAim]-                    , "cycle x-hair among enemies"-                    , AimEnemy ))-  , ("KP_Divide", ([CmdMinimal, CmdAim], "cycle x-hair among items", AimItem))-  , ("c", ( [CmdMinimal, CmdMove]-          , descTs closeDoorTriggers-          , AlterDir closeDoorTriggers ))-  , ("%", ([CmdMinimal, CmdMeta], "yell/yawn", Yell))+  , ("space", ( [CmdMinimal, CmdAim]+              , "clear messages/show history/cycle detail level"+              , ByAimMode AimModeCmd { exploration = ExecuteIfClear LastHistory+                                     , aiming = DetailCycle } ))+  , ("Tab", memberCycle Forward [CmdMinimal, CmdMove])+      -- listed here to keep proper order of the minimal cheat sheet+  , ("BackTab", memberCycle Backward [CmdMove])+  , ("A-Tab", memberCycleLevel Forward [])+  , ("A-BackTab", memberCycleLevel Backward [])+  , ("C-Tab", memberCycleLevel Forward [CmdMove])+  , ("C-BackTab", memberCycleLevel Backward [CmdMove])+      -- TODO: the keys are too long to fit in help menu, unless vertically+  , ("*", ( [CmdMinimal, CmdAim]+          , "cycle crosshair among enemies"+          , AimEnemy ))+  , ("/", ([CmdMinimal, CmdAim], "cycle crosshair among items", AimItem))+  , ("m", ([CmdMove], "modify door by closing it", CloseDir))+  , ("M", ([CmdMinimal, CmdMove], "modify any admissible terrain", AlterDir))+  , ("%", ([CmdMinimal, CmdMeta], "yell or yawn and stop sleeping", Yell))    -- Item menu, first part of item use commands-  , ("comma", grabItems "")-  , ("d", dropItems "drop item(s)")-  , ("period", dropItems "")+  , ("comma", grabItems "")  -- only show extra key, not extra entry+  , ("r", dropItems "remove item(s)")   , ("f", addCmdCategory CmdItemMenu $ projectA flingTs)   , ("C-f", addCmdCategory CmdItemMenu             $ replaceDesc "auto-fling and keep choice"             $ projectI flingTs)-  , ("a", addCmdCategory CmdItemMenu $ applyI applyTs)-  , ("C-a", addCmdCategory CmdItemMenu-            $ replaceDesc "apply and keep choice" $ applyIK applyTs)-  , ("p", moveItemTriple [CGround, CEqp, CSha] CInv-                         "item" False)-  , ("i", replaceDesc "" $ moveItemTriple [CGround, CEqp, CSha] CInv-                                          "item" False)-  , ("e", moveItemTriple [CGround, CInv, CSha] CEqp-                         "item" False)-  , ("s", moveItemTriple [CGround, CInv, CEqp] CSha-                         "and share item" False)--  -- Terrain exploration and alteration-  , ("C", ([CmdMove], "open or close or alter", AlterDir []))-  , ("=", ( [CmdMove], "select (or deselect) party member", SelectActor) )-  , ("_", ([CmdMove], "deselect (or select) all on the level", SelectNone))-  , ("semicolon", ( [CmdMove]-                  , "go to x-hair for 25 steps"-                  , Macro ["C-semicolon", "C-quotedbl", "C-V"] ))-  , ("colon", ( [CmdMove]-              , "run to x-hair collectively for 25 steps"-              , Macro ["C-colon", "C-quotedbl", "C-V"] ))-  , ("x", ( [CmdMove]-          , "explore nearest unknown spot"-          , autoexploreCmd ))-  , ("X", ( [CmdMove]-          , "autoexplore 25 times"-          , autoexplore25Cmd ))-  , ("R", ([CmdMove], "rest (wait 25 times)", Macro ["KP_Begin", "C-V"]))-  , ("C-R", ( [CmdMove], "heed (lurk 0.1 turns 100 times)"-            , Macro ["C-KP_Begin", "V"] ))+  , ("t", addCmdCategory CmdItemMenu $ applyI applyTs)+  , ("C-t", addCmdCategory CmdItemMenu+            $ replaceDesc "trigger item and keep choice" $ applyIK applyTs)+  , ("i", replaceDesc "stash item into shared inventory"+          $ moveItemTriple [CGround, CEqp] CStash "item" False)+  , ("o", replaceDesc "equip item into outfit of the pointman"+          $ moveItemTriple [CGround, CStash] CEqp "item" False) -  -- Item use, continued-  , ("P", ( [CmdItem, CmdDashboard]-          , "manage inventory pack of the leader"-          , ChooseItemMenu (MStore CInv) ))-  , ("I", ( [CmdItem, CmdDashboard]-          , ""-          , ChooseItemMenu (MStore CInv) ))-  , ("S", ( [CmdItem, CmdDashboard]-          , "manage the shared party stash"-          , ChooseItemMenu (MStore CSha) ))+  -- Remaining @ChooseItemMenu@ instances   , ("G", ( [CmdItem, CmdDashboard]           , "manage items on the ground"           , ChooseItemMenu (MStore CGround) ))-  , ("A", ( [CmdItem, CmdDashboard]-          , "manage all owned items"+  , ("T", ( [CmdItem, CmdDashboard]+          , "manage our total team belongings"           , ChooseItemMenu MOwned ))-  , ("@", ( [CmdItem, CmdDashboard]-          , "describe organs of the leader"+  , ("@", ( [CmdMeta, CmdDashboard]+          , "describe organs of the pointman"           , ChooseItemMenu MOrgans ))-  , ("#", ( [CmdItem, CmdDashboard]-          , "show skill summary of the leader"+  , ("#", ( [CmdMeta, CmdDashboard]+          , "show skill summary of the pointman"           , ChooseItemMenu MSkills ))-  , ("~", ( [CmdItem]-          , "display known lore"+  , ("~", ( [CmdMeta]+          , "display relevant lore"           , ChooseItemMenu (MLore SItem) ))    -- Dashboard, in addition to commands marked above@@ -155,56 +124,78 @@   map (\(k, slore) -> ("safeD" ++ show (k :: Int)                       , ( [CmdInternal, CmdDashboard]                         , "display" <+> ppSLore slore <+> "lore"+                          <+> if slore == SEmbed+                              then "(including crafting recipes)"+                              else ""                         , ChooseItemMenu (MLore slore) )))       (zip [1..] [minBound..maxBound])   ++-  [ ("safeD98", ( [CmdInternal, CmdDashboard]+  [ ("safeD97", ( [CmdInternal, CmdDashboard]                 , "display place lore"                 , ChooseItemMenu MPlaces) )+  , ("safeD98", ( [CmdInternal, CmdDashboard]+                , "display adventure lore"+                , ChooseItemMenu MModes) )   , ("safeD99", ([CmdInternal, CmdDashboard], "", Cancel))  -- blank line +  -- Terrain exploration and modification+  , ("=", ( [CmdMove], "select (or deselect) party member", SelectActor) )+  , ("_", ([CmdMove], "deselect (or select) all on the level", SelectNone))+  , ("semicolon", ( [CmdMove]+                  , "go to crosshair for 25 steps"+                  , Macro ["C-semicolon", "C-quotedbl", "C-v"] ))+  , ("colon", ( [CmdMove]+              , "run to crosshair collectively for 25 steps"+              , Macro ["C-colon", "C-quotedbl", "C-v"] ))+  , ("[", ( [CmdMove]+          , "explore nearest unknown spot"+          , autoexploreCmd ))+  , ("]", ( [CmdMove]+          , "autoexplore 25 times"+          , autoexplore25Cmd ))+  , ("R", ([CmdMove], "rest (wait 25 times)", Macro ["KP_Begin", "C-v"]))+  , ("C-R", ( [CmdMove], "heed (lurk 0.1 turns 100 times)"+            , Macro ["C-KP_Begin", "A-v"] ))+   -- Aiming-  , ("!", ([CmdAim], "", AimEnemy))-  , ("/", ([CmdAim], "", AimItem))-  , ("+", ([CmdAim], "swerve the aiming line", EpsIncr True))-  , ("-", ([CmdAim], "unswerve the aiming line", EpsIncr False))+  , ("+", ([CmdAim], "swerve the aiming line", EpsIncr Forward))+  , ("-", ([CmdAim], "unswerve the aiming line", EpsIncr Backward))   , ("\\", ([CmdAim], "cycle aiming modes", AimFloor))   , ("C-?", ( [CmdAim]-            , "set x-hair to nearest unknown spot"+            , "set crosshair to nearest unknown spot"             , XhairUnknown ))   , ("C-/", ( [CmdAim]-            , "set x-hair to nearest item"+            , "set crosshair to nearest item"             , XhairItem ))   , ("C-{", ( [CmdAim]-            , "set x-hair to nearest upstairs"+            , "aim at nearest upstairs"             , XhairStair True ))   , ("C-}", ( [CmdAim]-            , "set x-hair to nearest dnstairs"+            , "aim at nearest downstairs"             , XhairStair False ))   , ("<", ([CmdAim], "move aiming one level up" , AimAscend 1))-  , ("C-<", ( [CmdNoHelp], "move aiming 10 levels up"-            , AimAscend 10) )+  , ("C-<", ([], "move aiming 10 levels up", AimAscend 10))   , (">", ([CmdAim], "move aiming one level down", AimAscend (-1)))       -- 'lower' would be misleading in some games, just as 'deeper'-  , ("C->", ( [CmdNoHelp], "move aiming 10 levels down"-            , AimAscend (-10)) )+  , ("C->", ([], "move aiming 10 levels down", AimAscend (-10)))   , ("BackSpace" , ( [CmdAim]-                   , "clear chosen item and x-hair"+                   , "clear chosen item and crosshair"                    , ComposeUnlessError ClearTargetIfItemClear ItemClear))    -- Assorted (first few cloned from main menu)-  , ("C-s", ([CmdMeta], "start new game", GameRestart))+  , ("C-g", ([CmdMeta], "start new game", GameRestart))   , ("C-x", ([CmdMeta], "save and exit to desktop", GameExit))-  , ("C-t", ([CmdMeta], "toggle autoplay (insert coin)", Automate))   , ("C-q", ([CmdMeta], "quit game and start autoplay", GameQuit))   , ("C-c", ([CmdMeta], "exit to desktop without saving", GameDrop))   , ("?", ([CmdMeta], "display help", Hint))   , ("F1", ([CmdMeta, CmdDashboard], "display help immediately", Help))   , ("F12", ([CmdMeta], "open dashboard", Dashboard))-  , ("v", ([CmdMeta], "voice again the recorded commands", Repeat 1))-  , ("V", repeatTriple 100)-  , ("C-v", repeatTriple 1000)-  , ("C-V", repeatTriple 25)+  , ("v", repeatLastTriple 1 [CmdMeta])+  , ("C-v", repeatLastTriple 25 [])+  , ("A-v", repeatLastTriple 100 [])+  , ("V", repeatTriple 1 [CmdMeta])+  , ("C-V", repeatTriple 25 [])+  , ("A-V", repeatTriple 100 [])   , ("'", ([CmdMeta], "start recording commands", Record))   , ("C-S", ([CmdMeta], "save game backup", GameSave))   , ("C-P", ([CmdMeta], "print screen", PrintScreen))@@ -222,25 +213,26 @@   , ("RightButtonRelease", mouseRMB)   , ("C-LeftButtonRelease", replaceDesc "" mouseRMB)  -- Mac convention   , ( "S-RightButtonRelease"-    , ([CmdMouse], "open or close or alter at pointer", AlterWithPointer []) )+    , ([CmdMouse], "modify terrain at pointer", AlterWithPointer) )   , ("MiddleButtonRelease", mouseMMB)   , ("C-RightButtonRelease", replaceDesc "" mouseMMB)-  , ( "C-S-LeftButtonRelease",-      addCmdCategory CmdNoHelp $ replaceDesc "" mouseMMB )+  , ( "C-S-LeftButtonRelease", let (_, _, cmd) = mouseMMB+                               in ([], "", cmd) )+  , ("A-MiddleButtonRelease", mouseMMBMute)   , ("WheelNorth", ([CmdMouse], "swerve the aiming line", Macro ["+"]))   , ("WheelSouth", ([CmdMouse], "unswerve the aiming line", Macro ["-"]))    -- Debug and others not to display in help screens-  , ("C-semicolon", ( [CmdNoHelp]-                    , "move one step towards the x-hair"+  , ("C-semicolon", ( []+                    , "move one step towards the crosshair"                     , MoveOnceToXhair ))-  , ("C-colon", ( [CmdNoHelp]-                , "run collectively one step towards the x-hair"+  , ("C-colon", ( []+                , "run collectively one step towards the crosshair"                 , RunOnceToXhair ))-  , ("C-quotedbl", ( [CmdNoHelp]-                   , "continue towards the x-hair"+  , ("C-quotedbl", ( []+                   , "continue towards the crosshair"                    , ContinueToXhair ))-  , ("C-comma", ([CmdNoHelp], "run once ahead", RunOnceAhead))+  , ("C-comma", ([], "run once ahead", RunOnceAhead))   , ("safe1", ( [CmdInternal]               , "go to pointer for 25 steps"               , goToCmd ))@@ -248,13 +240,13 @@               , "run to pointer collectively"               , runToAllCmd ))   , ("safe3", ( [CmdInternal]-              , "pick new leader on screen"+              , "pick new pointman on screen"               , PickLeaderWithPointer ))   , ("safe4", ( [CmdInternal]               , "select party member on screen"               , SelectWithPointer ))   , ("safe5", ( [CmdInternal]-              , "set x-hair to enemy"+              , "set crosshair to enemy"               , AimPointerEnemy ))   , ("safe6", ( [CmdInternal]               , "fling at enemy under pointer"@@ -263,34 +255,30 @@               , "open main menu"               , MainMenuAutoOff ))   , ("safe8", ( [CmdInternal]+              , "clear msgs and open main menu"+              , ExecuteIfClear MainMenuAutoOff ))+  , ("safe9", ( [CmdInternal]               , "cancel aiming"               , Cancel ))-  , ("safe9", ( [CmdInternal]-              , "accept target"-              , Accept ))   , ("safe10", ( [CmdInternal]+               , "accept target"+               , Accept ))+  , ("safe11", ( [CmdInternal]+               , "show history"+               , LastHistory ))+  , ("safe12", ( [CmdInternal]                , "wait a turn, bracing for impact"                , Wait ))-  , ("safe11", ( [CmdInternal]+  , ("safe13", ( [CmdInternal]                , "lurk 0.1 of a turn"                , Wait10 ))-  , ("safe12", ( [CmdInternal]-               , "snap x-hair to enemy"+  , ("safe14", ( [CmdInternal]+               , "snap crosshair to enemy"                , XhairPointerEnemy ))   ]-  ++ map defaultHeroSelect [0..6]--closeDoorTriggers :: [TriggerTile]-closeDoorTriggers =-  [ TriggerTile { ttverb = "close"-                , ttobject = "door"-                , ttfeature = TK.CloseTo "closed door" }-  , TriggerTile { ttverb = "close"-                , ttobject = "door"-                , ttfeature = TK.CloseTo "closed door" }-  ]+  ++ map defaultHeroSelect [0..9]  applyTs :: [TriggerItem]-applyTs = [TriggerItem { tiverb = "apply"-                       , tiobject = "consumable"+applyTs = [TriggerItem { tiverb = "trigger"+                       , tiobject = "consumable item"                        , tisymbols = "!,?-" }]
GameDefinition/game-src/Client/UI/Content/Screen.hs view
@@ -1,5 +1,5 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE.@@ -15,6 +15,7 @@ import Game.LambdaHack.Core.Prelude  import qualified Data.EnumMap.Strict as EM+import           Data.FileEmbed (embedDir) import           Language.Haskell.TH.Syntax import           System.IO @@ -25,24 +26,8 @@ standardLayoutAndFeatures = ScreenContent   { rwidth = 80   , rheight = 45-  -- ASCII art for the main menu. Only pure 7-bit ASCII characters are allowed,-  -- except for character 183 ('·'), which is rendered as very tiny middle dot.-  -- The encoding should be utf-8-unix.-  -- When displayed in the main menu screen, the picture is overwritten-  -- with game and engine version strings and keybindings.-  -- The keybindings overwrite places marked with left curly brace signs.-  -- This sign is forbidden anywhere else in the picture.-  -- The picture and the whole main menu is displayed dull white on black.-  -- The glyphs, or at least the character cells, are perfect squares.-  -- The picture for Allure should be exactly 45 rows by 80 columns.-  , rmainMenuArt = $(do-      let path = "GameDefinition/MainMenu.ascii"-      qAddDependentFile path-      x <- qRunIO $ do-        handle <- openFile path ReadMode-        hSetEncoding handle utf8-        hGetContents handle-      lift x)+  , rwrap = 53  -- more with proportional font and then ideal reading comfort+  , rwebAddress = "http://allureofthestars.com"   , rintroScreen = $(do       let path = "GameDefinition/PLAYING.md"       qAddDependentFile path@@ -52,24 +37,29 @@         hGetContents handle       let paragraphs :: [String] -> [String] -> [[String]]           paragraphs [] rows = [reverse rows]-          paragraphs (l : ls) rows = if null l-                                     then reverse rows : paragraphs ls []-                                     else paragraphs ls (l : rows)+          paragraphs (l@"" : ls) rows = case (rows, ls) of+            (('=':'=' : _) : _, _) ->  -- A title. No new paragraph.+              paragraphs ls (l : rows)+            (('-':'-' : _) : _, _) ->  -- A title. No new paragraph.+              paragraphs ls (l : rows)+            ((' ':' ':' ':' ' : _) : _, (' ':' ':' ':' ' : _) : _) ->+              -- At least four spaces before and after; probably a code block.+              paragraphs ls (l : rows)+            _ -> reverse rows : paragraphs ls []+          paragraphs (l : ls) rows = paragraphs ls (l : rows)           intro = case paragraphs (lines x) [] of-            _title : _blurb : par1 : par2 : par3 : _rest ->-              ["", "", ""] ++ par1-              ++ [""] ++ par2-              ++ [""] ++ par3 ++ ["", "", "", "", ""]+            _titleAndBlurb : par1 : par2 : par3 : rest ->+              (par1 ++ [""] ++ par2 ++ [""] ++ par3, rest)             _ -> error "not enough paragraphs in intro screen text"       lift intro)-  , rmoveKeysScreen = $(do-      let path = "GameDefinition/MoveKeys.txt"-      qAddDependentFile path-      x <- qRunIO $ do-        handle <- openFile path ReadMode-        hSetEncoding handle utf8-        hGetContents handle-      lift $ lines x)   , rapplyVerbMap =       EM.fromList [('!', "imbibe"), (',', "eat"), ('?', "activate")]+  , rFontFiles =+-- Checking USE_SDL would be more accurate, but would require complicating+-- .cabal file and slightly larger vty executable is not a problem.+#ifdef USE_JSFILE+      []+#else+      $(embedDir "GameDefinition/fonts")+#endif   }
GameDefinition/game-src/Implementation/MonadClientImplementation.hs view
@@ -1,5 +1,5 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE.@@ -33,6 +33,7 @@ import           Game.LambdaHack.Client.State import           Game.LambdaHack.Client.UI import           Game.LambdaHack.Client.UI.SessionUI+import           Game.LambdaHack.Common.ClientOptions import           Game.LambdaHack.Common.Kind import           Game.LambdaHack.Common.MonadStateRead import qualified Game.LambdaHack.Common.Save as Save@@ -85,22 +86,6 @@     cli <- gets cliClient     msess <- gets cliSession     IO.liftIO $ Save.saveToChan toSave (cli, msess)-  restartClient = CliImplementation $ state $ \cliS ->-    case cliSession cliS of-      Just sess ->-        let !newSess = (emptySessionUI (sUIOptions sess))-                         { schanF = schanF sess-                         , sccui = sccui sess-                         , shistory = shistory sess-                         , sstart = sstart sess-                         , sgstart = sgstart sess-                         , sallTime = sallTime sess-                         , snframes = snframes sess-                         , sallNframes = sallNframes sess-                         }-            !newCliS = cliS {cliSession = Just newSess}-        in ((), newCliS)-      Nothing -> ((), cliS)  instance MonadClientUI CliImplementation where   {-# INLINE getsSession #-}@@ -147,11 +132,11 @@             -> FactionId             -> ChanServer             -> IO ()-executorCli ccui sUIOptions clientOptions cops isUI fid cliDict =+executorCli ccui sUIOptions clientOptions cops@COps{corule} isUI fid cliDict =   let cliSession | isUI = Just $ emptySessionUI sUIOptions                  | otherwise = Nothing       stateToFileName (cli, _) =-        ssavePrefixCli (soptions cli) <> Save.saveNameCli cops (sside cli)+        ssavePrefixCli (soptions cli) <> Save.saveNameCli corule (sside cli)       totalState cliToSave = CliState         { cliState = updateCOpsAndCachedData (const cops) emptyState             -- state is empty, so the cached data is left empty and untouched
GameDefinition/game-src/Implementation/MonadServerImplementation.hs view
@@ -1,5 +1,5 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE.@@ -25,17 +25,14 @@ import           Control.Monad.Trans.State.Strict hiding (State) import qualified Data.EnumMap.Strict as EM import qualified Data.Text.IO as T-import           Options.Applicative (defaultPrefs, execParserPure,-                                      handleParseResult)+import           Options.Applicative+  (defaultPrefs, execParserPure, handleParseResult) import           System.Exit (ExitCode (ExitSuccess))-import           System.FilePath import           System.IO (hFlush, stdout)  import           Game.LambdaHack.Atomic import           Game.LambdaHack.Client-import           Game.LambdaHack.Common.File import           Game.LambdaHack.Common.Kind-import           Game.LambdaHack.Common.Misc import           Game.LambdaHack.Common.MonadStateRead import qualified Game.LambdaHack.Common.Save as Save import           Game.LambdaHack.Common.State@@ -45,7 +42,6 @@ import           Game.LambdaHack.Server.HandleAtomicM import           Game.LambdaHack.Server.MonadServer import           Game.LambdaHack.Server.ProtocolM-import           Game.LambdaHack.Server.ServerOptions import           Game.LambdaHack.Server.State  import Implementation.MonadClientImplementation (executorCli)@@ -146,7 +142,7 @@ -- | Run the main server loop, with the given arguments and empty -- initial states, in the @IO@ monad. executorSer :: COps -> CCUI -> ServerOptions -> UIOptions -> IO ()-executorSer cops ccui soptionsNxtCmdline sUIOptions = do+executorSer cops@COps{corule} ccui soptionsNxtCmdline sUIOptions = do   soptionsNxtRaw <- case uCmdline sUIOptions of     []   -> return soptionsNxtCmdline     args -> handleParseResult $ execParserPure defaultPrefs serverOptionsPI args@@ -159,7 +155,7 @@   -- Wire together game content, the main loop of game clients   -- and the game server loop.   let stateToFileName (_, ser) =-        ssavePrefixSer (soptions ser) <> Save.saveNameSer cops+        ssavePrefixSer (soptions ser) <> Save.saveNameSer corule       totalState serToSave = SerState         { serState = updateCOpsAndCachedData (const cops) emptyState             -- state is empty, so the cached data is left empty and untouched@@ -170,17 +166,6 @@       m = loopSer soptionsNxt executorClient       exe = evalStateT (runSerImplementation m) . totalState       exeWithSaves = Save.wrapInSaves cops stateToFileName exe-      defPrefix = ssavePrefixSer defServerOptions-      bkpOneSave name = do-        dataDir <- appDataDir-        let path bkp = dataDir </> "saves" </> bkp <> name-        b <- doesFileExist (path "")-        when b $ renameFile (path "") (path "bkp.")-      bkpAllSaves = unless (sbenchmark $ sclientOptions soptionsNxt) $ do-        T.hPutStrLn stdout "The game crashed, so savefiles are moved aside."-        bkpOneSave $ defPrefix <> Save.saveNameSer cops-        forM_ [-99..99] $ \n ->-          bkpOneSave $ defPrefix <> Save.saveNameCli cops (toEnum n)   -- Wait for clients to exit even in case of server crash   -- (or server and client crash), which gives them time to save   -- and report their own inconsistencies, if any.@@ -192,7 +177,10 @@                _ -> do                  Ex.uninterruptibleMask_ $ threadDelay 1000000                    -- let clients report their errors and save-                 when (ssavePrefixSer soptionsNxt == defPrefix) bkpAllSaves+                 moveAside <- Save.bkpAllSaves corule clientOptions+                 when moveAside $+                   T.hPutStrLn stdout+                               "The game crashed, so savefiles are moved aside."                  hFlush stdout                  Ex.throwIO ex  -- crash eventually, which kills clients             )
GameDefinition/game-src/TieKnot.hs view
@@ -1,5 +1,5 @@ -- Copyright (c) 2008--2011 Andres Loeh--- Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+-- Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history) -- This file is a part of the computer game Allure of the Stars -- and is released under the terms of the GNU Affero General Public License. -- For license and copyright information, see the file LICENSE.@@ -19,7 +19,7 @@ import qualified Control.Exception as Ex import qualified Data.Primitive.PrimArray as PA import           GHC.Compact-import qualified System.Random as R+import qualified System.Random.SplitMix32 as SM  import           Game.LambdaHack.Client import qualified Game.LambdaHack.Client.UI.Content.Input as IC@@ -68,33 +68,41 @@   -- This setup ensures the boosting option doesn't affect generating initial   -- RNG for dungeon, etc., and also, that setting dungeon RNG on commandline   -- equal to what was generated last time, ensures the same item boost.-  initialGen <- maybe R.getStdGen return sdungeonRng+  initialGen <- maybe SM.newSMGen return sdungeonRng   let soptionsNxt = options {sdungeonRng = Just initialGen}       boostedItems = IK.boostItemKindList initialGen Content.ItemKind.items-      coitem = IK.makeData $+      itemContent =         if sboostRandomItem         then boostedItems ++ Content.ItemKind.otherItemContent         else Content.ItemKind.content+      coitem = IK.makeData itemContent+                           Content.ItemKind.groupNamesSingleton+                           Content.ItemKind.groupNames       coItemSpeedup = speedupItem coitem-      cotile = TK.makeData coitem Content.TileKind.content+      cotile = TK.makeData Content.TileKind.content+                           Content.TileKind.groupNamesSingleton+                           Content.TileKind.groupNames       coTileSpeedup = Tile.speedupTile sallClear cotile-      coplace = PK.makeData cotile Content.PlaceKind.content-      cocave = CK.makeData coitem coplace cotile Content.CaveKind.content       -- Common content operations, created from content definitions.       -- Evaluated fully to discover errors ASAP and to free memory.       -- Fail here, not inside server code, so that savefiles are not removed,       -- because they are not the source of the failure.       copsRaw = COps-        { cocave+        { cocave = CK.makeData Content.CaveKind.content+                               Content.CaveKind.groupNamesSingleton+                               Content.CaveKind.groupNames         , coitem-        , comode  = MK.makeData cocave coitem Content.ModeKind.content-        , coplace-        , corule  = RK.makeData Content.RuleKind.standardRules+        , comode = MK.makeData Content.ModeKind.content+                               Content.ModeKind.groupNamesSingleton+                               Content.ModeKind.groupNames+        , coplace = PK.makeData Content.PlaceKind.content+                                Content.PlaceKind.groupNamesSingleton+                                Content.PlaceKind.groupNames+        , corule = RK.makeData Content.RuleKind.standardRules         , cotile         , coItemSpeedup         , coTileSpeedup         }-      benchmark = sbenchmark $ sclientOptions soptionsNxt   -- Evaluating for compact regions catches all kinds of errors in content ASAP,   -- even in unused items.   --@@ -109,11 +117,12 @@   -- It is reparsed at each start of the game executable.   -- Fail here, not inside client code, so that savefiles are not removed,   -- because they are not the source of the failure.-  sUIOptions <- mkUIOptions cops benchmark+  sUIOptions <- mkUIOptions (corule cops) (sclientOptions soptionsNxt)   -- Client content operations containing default keypresses   -- and command descriptions.   let !ccui = CCUI-        { coinput = IC.makeData sUIOptions Content.Input.standardKeysAndMouse+        { coinput = IC.makeData (Just sUIOptions)+                                Content.Input.standardKeysAndMouse         , coscreen = SC.makeData Content.Screen.standardLayoutAndFeatures         }   -- Wire together game content, the main loops of game clients@@ -135,11 +144,15 @@   -- Avoid the bound thread that would slow down the communication.   a <- async $ tieKnotForAsync serverOptions                `Ex.finally` fillWorkaround+  -- Exit on an exception without waiting for frontend to spawn.   link a   -- Run a (possibly void) workaround. It's needed for OSes/frontends   -- that need to perform some actions on the main thread   -- (not just any bound thread), e.g., newer OS X drawing with SDL2.-  workaround <- takeMVar workaroundOnMainThreadMVar-  workaround+  join (takeMVar workaroundOnMainThreadMVar)+  -- Wait in case frontend workaround not run on the main thread+  -- and so we'd exit too early and end the game.   wait a+  -- Consume the void workaround if it was spurious to make @tieKnot@ reentrant.+  void $ tryTakeMVar workaroundOnMainThreadMVar #endif
− LICENSE
@@ -1,661 +0,0 @@-                    GNU AFFERO GENERAL PUBLIC LICENSE-                       Version 3, 19 November 2007-- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>- Everyone is permitted to copy and distribute verbatim copies- of this license document, but changing it is not allowed.--                            Preamble--  The GNU Affero General Public License is a free, copyleft license for-software and other kinds of works, specifically designed to ensure-cooperation with the community in the case of network server software.--  The licenses for most software and other practical works are designed-to take away your freedom to share and change the works.  By contrast,-our General Public Licenses are intended to guarantee your freedom to-share and change all versions of a program--to make sure it remains free-software for all its users.--  When we speak of free software, we are referring to freedom, not-price.  Our General Public Licenses are designed to make sure that you-have the freedom to distribute copies of free software (and charge for-them if you wish), that you receive source code or can get it if you-want it, that you can change the software or use pieces of it in new-free programs, and that you know you can do these things.--  Developers that use our General Public Licenses protect your rights-with two steps: (1) assert copyright on the software, and (2) offer-you this License which gives you legal permission to copy, distribute-and/or modify the software.--  A secondary benefit of defending all users' freedom is that-improvements made in alternate versions of the program, if they-receive widespread use, become available for other developers to-incorporate.  Many developers of free software are heartened and-encouraged by the resulting cooperation.  However, in the case of-software used on network servers, this result may fail to come about.-The GNU General Public License permits making a modified version and-letting the public access it on a server without ever releasing its-source code to the public.--  The GNU Affero General Public License is designed specifically to-ensure that, in such cases, the modified source code becomes available-to the community.  It requires the operator of a network server to-provide the source code of the modified version running there to the-users of that server.  Therefore, public use of a modified version, on-a publicly accessible server, gives the public access to the source-code of the modified version.--  An older license, called the Affero General Public License and-published by Affero, was designed to accomplish similar goals.  This is-a different license, not a version of the Affero GPL, but Affero has-released a new version of the Affero GPL which permits relicensing under-this license.--  The precise terms and conditions for copying, distribution and-modification follow.--                       TERMS AND CONDITIONS--  0. Definitions.--  "This License" refers to version 3 of the GNU Affero General Public License.--  "Copyright" also means copyright-like laws that apply to other kinds of-works, such as semiconductor masks.--  "The Program" refers to any copyrightable work licensed under this-License.  Each licensee is addressed as "you".  "Licensees" and-"recipients" may be individuals or organizations.--  To "modify" a work means to copy from or adapt all or part of the work-in a fashion requiring copyright permission, other than the making of an-exact copy.  The resulting work is called a "modified version" of the-earlier work or a work "based on" the earlier work.--  A "covered work" means either the unmodified Program or a work based-on the Program.--  To "propagate" a work means to do anything with it that, without-permission, would make you directly or secondarily liable for-infringement under applicable copyright law, except executing it on a-computer or modifying a private copy.  Propagation includes copying,-distribution (with or without modification), making available to the-public, and in some countries other activities as well.--  To "convey" a work means any kind of propagation that enables other-parties to make or receive copies.  Mere interaction with a user through-a computer network, with no transfer of a copy, is not conveying.--  An interactive user interface displays "Appropriate Legal Notices"-to the extent that it includes a convenient and prominently visible-feature that (1) displays an appropriate copyright notice, and (2)-tells the user that there is no warranty for the work (except to the-extent that warranties are provided), that licensees may convey the-work under this License, and how to view a copy of this License.  If-the interface presents a list of user commands or options, such as a-menu, a prominent item in the list meets this criterion.--  1. Source Code.--  The "source code" for a work means the preferred form of the work-for making modifications to it.  "Object code" means any non-source-form of a work.--  A "Standard Interface" means an interface that either is an official-standard defined by a recognized standards body, or, in the case of-interfaces specified for a particular programming language, one that-is widely used among developers working in that language.--  The "System Libraries" of an executable work include anything, other-than the work as a whole, that (a) is included in the normal form of-packaging a Major Component, but which is not part of that Major-Component, and (b) serves only to enable use of the work with that-Major Component, or to implement a Standard Interface for which an-implementation is available to the public in source code form.  A-"Major Component", in this context, means a major essential component-(kernel, window system, and so on) of the specific operating system-(if any) on which the executable work runs, or a compiler used to-produce the work, or an object code interpreter used to run it.--  The "Corresponding Source" for a work in object code form means all-the source code needed to generate, install, and (for an executable-work) run the object code and to modify the work, including scripts to-control those activities.  However, it does not include the work's-System Libraries, or general-purpose tools or generally available free-programs which are used unmodified in performing those activities but-which are not part of the work.  For example, Corresponding Source-includes interface definition files associated with source files for-the work, and the source code for shared libraries and dynamically-linked subprograms that the work is specifically designed to require,-such as by intimate data communication or control flow between those-subprograms and other parts of the work.--  The Corresponding Source need not include anything that users-can regenerate automatically from other parts of the Corresponding-Source.--  The Corresponding Source for a work in source code form is that-same work.--  2. Basic Permissions.--  All rights granted under this License are granted for the term of-copyright on the Program, and are irrevocable provided the stated-conditions are met.  This License explicitly affirms your unlimited-permission to run the unmodified Program.  The output from running a-covered work is covered by this License only if the output, given its-content, constitutes a covered work.  This License acknowledges your-rights of fair use or other equivalent, as provided by copyright law.--  You may make, run and propagate covered works that you do not-convey, without conditions so long as your license otherwise remains-in force.  You may convey covered works to others for the sole purpose-of having them make modifications exclusively for you, or provide you-with facilities for running those works, provided that you comply with-the terms of this License in conveying all material for which you do-not control copyright.  Those thus making or running the covered works-for you must do so exclusively on your behalf, under your direction-and control, on terms that prohibit them from making any copies of-your copyrighted material outside their relationship with you.--  Conveying under any other circumstances is permitted solely under-the conditions stated below.  Sublicensing is not allowed; section 10-makes it unnecessary.--  3. Protecting Users' Legal Rights From Anti-Circumvention Law.--  No covered work shall be deemed part of an effective technological-measure under any applicable law fulfilling obligations under article-11 of the WIPO copyright treaty adopted on 20 December 1996, or-similar laws prohibiting or restricting circumvention of such-measures.--  When you convey a covered work, you waive any legal power to forbid-circumvention of technological measures to the extent such circumvention-is effected by exercising rights under this License with respect to-the covered work, and you disclaim any intention to limit operation or-modification of the work as a means of enforcing, against the work's-users, your or third parties' legal rights to forbid circumvention of-technological measures.--  4. Conveying Verbatim Copies.--  You may convey verbatim copies of the Program's source code as you-receive it, in any medium, provided that you conspicuously and-appropriately publish on each copy an appropriate copyright notice;-keep intact all notices stating that this License and any-non-permissive terms added in accord with section 7 apply to the code;-keep intact all notices of the absence of any warranty; and give all-recipients a copy of this License along with the Program.--  You may charge any price or no price for each copy that you convey,-and you may offer support or warranty protection for a fee.--  5. Conveying Modified Source Versions.--  You may convey a work based on the Program, or the modifications to-produce it from the Program, in the form of source code under the-terms of section 4, provided that you also meet all of these conditions:--    a) The work must carry prominent notices stating that you modified-    it, and giving a relevant date.--    b) The work must carry prominent notices stating that it is-    released under this License and any conditions added under section-    7.  This requirement modifies the requirement in section 4 to-    "keep intact all notices".--    c) You must license the entire work, as a whole, under this-    License to anyone who comes into possession of a copy.  This-    License will therefore apply, along with any applicable section 7-    additional terms, to the whole of the work, and all its parts,-    regardless of how they are packaged.  This License gives no-    permission to license the work in any other way, but it does not-    invalidate such permission if you have separately received it.--    d) If the work has interactive user interfaces, each must display-    Appropriate Legal Notices; however, if the Program has interactive-    interfaces that do not display Appropriate Legal Notices, your-    work need not make them do so.--  A compilation of a covered work with other separate and independent-works, which are not by their nature extensions of the covered work,-and which are not combined with it such as to form a larger program,-in or on a volume of a storage or distribution medium, is called an-"aggregate" if the compilation and its resulting copyright are not-used to limit the access or legal rights of the compilation's users-beyond what the individual works permit.  Inclusion of a covered work-in an aggregate does not cause this License to apply to the other-parts of the aggregate.--  6. Conveying Non-Source Forms.--  You may convey a covered work in object code form under the terms-of sections 4 and 5, provided that you also convey the-machine-readable Corresponding Source under the terms of this License,-in one of these ways:--    a) Convey the object code in, or embodied in, a physical product-    (including a physical distribution medium), accompanied by the-    Corresponding Source fixed on a durable physical medium-    customarily used for software interchange.--    b) Convey the object code in, or embodied in, a physical product-    (including a physical distribution medium), accompanied by a-    written offer, valid for at least three years and valid for as-    long as you offer spare parts or customer support for that product-    model, to give anyone who possesses the object code either (1) a-    copy of the Corresponding Source for all the software in the-    product that is covered by this License, on a durable physical-    medium customarily used for software interchange, for a price no-    more than your reasonable cost of physically performing this-    conveying of source, or (2) access to copy the-    Corresponding Source from a network server at no charge.--    c) Convey individual copies of the object code with a copy of the-    written offer to provide the Corresponding Source.  This-    alternative is allowed only occasionally and noncommercially, and-    only if you received the object code with such an offer, in accord-    with subsection 6b.--    d) Convey the object code by offering access from a designated-    place (gratis or for a charge), and offer equivalent access to the-    Corresponding Source in the same way through the same place at no-    further charge.  You need not require recipients to copy the-    Corresponding Source along with the object code.  If the place to-    copy the object code is a network server, the Corresponding Source-    may be on a different server (operated by you or a third party)-    that supports equivalent copying facilities, provided you maintain-    clear directions next to the object code saying where to find the-    Corresponding Source.  Regardless of what server hosts the-    Corresponding Source, you remain obligated to ensure that it is-    available for as long as needed to satisfy these requirements.--    e) Convey the object code using peer-to-peer transmission, provided-    you inform other peers where the object code and Corresponding-    Source of the work are being offered to the general public at no-    charge under subsection 6d.--  A separable portion of the object code, whose source code is excluded-from the Corresponding Source as a System Library, need not be-included in conveying the object code work.--  A "User Product" is either (1) a "consumer product", which means any-tangible personal property which is normally used for personal, family,-or household purposes, or (2) anything designed or sold for incorporation-into a dwelling.  In determining whether a product is a consumer product,-doubtful cases shall be resolved in favor of coverage.  For a particular-product received by a particular user, "normally used" refers to a-typical or common use of that class of product, regardless of the status-of the particular user or of the way in which the particular user-actually uses, or expects or is expected to use, the product.  A product-is a consumer product regardless of whether the product has substantial-commercial, industrial or non-consumer uses, unless such uses represent-the only significant mode of use of the product.--  "Installation Information" for a User Product means any methods,-procedures, authorization keys, or other information required to install-and execute modified versions of a covered work in that User Product from-a modified version of its Corresponding Source.  The information must-suffice to ensure that the continued functioning of the modified object-code is in no case prevented or interfered with solely because-modification has been made.--  If you convey an object code work under this section in, or with, or-specifically for use in, a User Product, and the conveying occurs as-part of a transaction in which the right of possession and use of the-User Product is transferred to the recipient in perpetuity or for a-fixed term (regardless of how the transaction is characterized), the-Corresponding Source conveyed under this section must be accompanied-by the Installation Information.  But this requirement does not apply-if neither you nor any third party retains the ability to install-modified object code on the User Product (for example, the work has-been installed in ROM).--  The requirement to provide Installation Information does not include a-requirement to continue to provide support service, warranty, or updates-for a work that has been modified or installed by the recipient, or for-the User Product in which it has been modified or installed.  Access to a-network may be denied when the modification itself materially and-adversely affects the operation of the network or violates the rules and-protocols for communication across the network.--  Corresponding Source conveyed, and Installation Information provided,-in accord with this section must be in a format that is publicly-documented (and with an implementation available to the public in-source code form), and must require no special password or key for-unpacking, reading or copying.--  7. Additional Terms.--  "Additional permissions" are terms that supplement the terms of this-License by making exceptions from one or more of its conditions.-Additional permissions that are applicable to the entire Program shall-be treated as though they were included in this License, to the extent-that they are valid under applicable law.  If additional permissions-apply only to part of the Program, that part may be used separately-under those permissions, but the entire Program remains governed by-this License without regard to the additional permissions.--  When you convey a copy of a covered work, you may at your option-remove any additional permissions from that copy, or from any part of-it.  (Additional permissions may be written to require their own-removal in certain cases when you modify the work.)  You may place-additional permissions on material, added by you to a covered work,-for which you have or can give appropriate copyright permission.--  Notwithstanding any other provision of this License, for material you-add to a covered work, you may (if authorized by the copyright holders of-that material) supplement the terms of this License with terms:--    a) Disclaiming warranty or limiting liability differently from the-    terms of sections 15 and 16 of this License; or--    b) Requiring preservation of specified reasonable legal notices or-    author attributions in that material or in the Appropriate Legal-    Notices displayed by works containing it; or--    c) Prohibiting misrepresentation of the origin of that material, or-    requiring that modified versions of such material be marked in-    reasonable ways as different from the original version; or--    d) Limiting the use for publicity purposes of names of licensors or-    authors of the material; or--    e) Declining to grant rights under trademark law for use of some-    trade names, trademarks, or service marks; or--    f) Requiring indemnification of licensors and authors of that-    material by anyone who conveys the material (or modified versions of-    it) with contractual assumptions of liability to the recipient, for-    any liability that these contractual assumptions directly impose on-    those licensors and authors.--  All other non-permissive additional terms are considered "further-restrictions" within the meaning of section 10.  If the Program as you-received it, or any part of it, contains a notice stating that it is-governed by this License along with a term that is a further-restriction, you may remove that term.  If a license document contains-a further restriction but permits relicensing or conveying under this-License, you may add to a covered work material governed by the terms-of that license document, provided that the further restriction does-not survive such relicensing or conveying.--  If you add terms to a covered work in accord with this section, you-must place, in the relevant source files, a statement of the-additional terms that apply to those files, or a notice indicating-where to find the applicable terms.--  Additional terms, permissive or non-permissive, may be stated in the-form of a separately written license, or stated as exceptions;-the above requirements apply either way.--  8. Termination.--  You may not propagate or modify a covered work except as expressly-provided under this License.  Any attempt otherwise to propagate or-modify it is void, and will automatically terminate your rights under-this License (including any patent licenses granted under the third-paragraph of section 11).--  However, if you cease all violation of this License, then your-license from a particular copyright holder is reinstated (a)-provisionally, unless and until the copyright holder explicitly and-finally terminates your license, and (b) permanently, if the copyright-holder fails to notify you of the violation by some reasonable means-prior to 60 days after the cessation.--  Moreover, your license from a particular copyright holder is-reinstated permanently if the copyright holder notifies you of the-violation by some reasonable means, this is the first time you have-received notice of violation of this License (for any work) from that-copyright holder, and you cure the violation prior to 30 days after-your receipt of the notice.--  Termination of your rights under this section does not terminate the-licenses of parties who have received copies or rights from you under-this License.  If your rights have been terminated and not permanently-reinstated, you do not qualify to receive new licenses for the same-material under section 10.--  9. Acceptance Not Required for Having Copies.--  You are not required to accept this License in order to receive or-run a copy of the Program.  Ancillary propagation of a covered work-occurring solely as a consequence of using peer-to-peer transmission-to receive a copy likewise does not require acceptance.  However,-nothing other than this License grants you permission to propagate or-modify any covered work.  These actions infringe copyright if you do-not accept this License.  Therefore, by modifying or propagating a-covered work, you indicate your acceptance of this License to do so.--  10. Automatic Licensing of Downstream Recipients.--  Each time you convey a covered work, the recipient automatically-receives a license from the original licensors, to run, modify and-propagate that work, subject to this License.  You are not responsible-for enforcing compliance by third parties with this License.--  An "entity transaction" is a transaction transferring control of an-organization, or substantially all assets of one, or subdividing an-organization, or merging organizations.  If propagation of a covered-work results from an entity transaction, each party to that-transaction who receives a copy of the work also receives whatever-licenses to the work the party's predecessor in interest had or could-give under the previous paragraph, plus a right to possession of the-Corresponding Source of the work from the predecessor in interest, if-the predecessor has it or can get it with reasonable efforts.--  You may not impose any further restrictions on the exercise of the-rights granted or affirmed under this License.  For example, you may-not impose a license fee, royalty, or other charge for exercise of-rights granted under this License, and you may not initiate litigation-(including a cross-claim or counterclaim in a lawsuit) alleging that-any patent claim is infringed by making, using, selling, offering for-sale, or importing the Program or any portion of it.--  11. Patents.--  A "contributor" is a copyright holder who authorizes use under this-License of the Program or a work on which the Program is based.  The-work thus licensed is called the contributor's "contributor version".--  A contributor's "essential patent claims" are all patent claims-owned or controlled by the contributor, whether already acquired or-hereafter acquired, that would be infringed by some manner, permitted-by this License, of making, using, or selling its contributor version,-but do not include claims that would be infringed only as a-consequence of further modification of the contributor version.  For-purposes of this definition, "control" includes the right to grant-patent sublicenses in a manner consistent with the requirements of-this License.--  Each contributor grants you a non-exclusive, worldwide, royalty-free-patent license under the contributor's essential patent claims, to-make, use, sell, offer for sale, import and otherwise run, modify and-propagate the contents of its contributor version.--  In the following three paragraphs, a "patent license" is any express-agreement or commitment, however denominated, not to enforce a patent-(such as an express permission to practice a patent or covenant not to-sue for patent infringement).  To "grant" such a patent license to a-party means to make such an agreement or commitment not to enforce a-patent against the party.--  If you convey a covered work, knowingly relying on a patent license,-and the Corresponding Source of the work is not available for anyone-to copy, free of charge and under the terms of this License, through a-publicly available network server or other readily accessible means,-then you must either (1) cause the Corresponding Source to be so-available, or (2) arrange to deprive yourself of the benefit of the-patent license for this particular work, or (3) arrange, in a manner-consistent with the requirements of this License, to extend the patent-license to downstream recipients.  "Knowingly relying" means you have-actual knowledge that, but for the patent license, your conveying the-covered work in a country, or your recipient's use of the covered work-in a country, would infringe one or more identifiable patents in that-country that you have reason to believe are valid.--  If, pursuant to or in connection with a single transaction or-arrangement, you convey, or propagate by procuring conveyance of, a-covered work, and grant a patent license to some of the parties-receiving the covered work authorizing them to use, propagate, modify-or convey a specific copy of the covered work, then the patent license-you grant is automatically extended to all recipients of the covered-work and works based on it.--  A patent license is "discriminatory" if it does not include within-the scope of its coverage, prohibits the exercise of, or is-conditioned on the non-exercise of one or more of the rights that are-specifically granted under this License.  You may not convey a covered-work if you are a party to an arrangement with a third party that is-in the business of distributing software, under which you make payment-to the third party based on the extent of your activity of conveying-the work, and under which the third party grants, to any of the-parties who would receive the covered work from you, a discriminatory-patent license (a) in connection with copies of the covered work-conveyed by you (or copies made from those copies), or (b) primarily-for and in connection with specific products or compilations that-contain the covered work, unless you entered into that arrangement,-or that patent license was granted, prior to 28 March 2007.--  Nothing in this License shall be construed as excluding or limiting-any implied license or other defenses to infringement that may-otherwise be available to you under applicable patent law.--  12. No Surrender of Others' Freedom.--  If conditions are imposed on you (whether by court order, agreement or-otherwise) that contradict the conditions of this License, they do not-excuse you from the conditions of this License.  If you cannot convey a-covered work so as to satisfy simultaneously your obligations under this-License and any other pertinent obligations, then as a consequence you may-not convey it at all.  For example, if you agree to terms that obligate you-to collect a royalty for further conveying from those to whom you convey-the Program, the only way you could satisfy both those terms and this-License would be to refrain entirely from conveying the Program.--  13. Remote Network Interaction; Use with the GNU General Public License.--  Notwithstanding any other provision of this License, if you modify the-Program, your modified version must prominently offer all users-interacting with it remotely through a computer network (if your version-supports such interaction) an opportunity to receive the Corresponding-Source of your version by providing access to the Corresponding Source-from a network server at no charge, through some standard or customary-means of facilitating copying of software.  This Corresponding Source-shall include the Corresponding Source for any work covered by version 3-of the GNU General Public License that is incorporated pursuant to the-following paragraph.--  Notwithstanding any other provision of this License, you have-permission to link or combine any covered work with a work licensed-under version 3 of the GNU General Public License into a single-combined work, and to convey the resulting work.  The terms of this-License will continue to apply to the part which is the covered work,-but the work with which it is combined will remain governed by version-3 of the GNU General Public License.--  14. Revised Versions of this License.--  The Free Software Foundation may publish revised and/or new versions of-the GNU Affero General Public License from time to time.  Such new versions-will be similar in spirit to the present version, but may differ in detail to-address new problems or concerns.--  Each version is given a distinguishing version number.  If the-Program specifies that a certain numbered version of the GNU Affero General-Public License "or any later version" applies to it, you have the-option of following the terms and conditions either of that numbered-version or of any later version published by the Free Software-Foundation.  If the Program does not specify a version number of the-GNU Affero General Public License, you may choose any version ever published-by the Free Software Foundation.--  If the Program specifies that a proxy can decide which future-versions of the GNU Affero General Public License can be used, that proxy's-public statement of acceptance of a version permanently authorizes you-to choose that version for the Program.--  Later license versions may give you additional or different-permissions.  However, no additional obligations are imposed on any-author or copyright holder as a result of your choosing to follow a-later version.--  15. Disclaimer of Warranty.--  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY-APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM-IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.--  16. Limitation of Liability.--  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF-SUCH DAMAGES.--  17. Interpretation of Sections 15 and 16.--  If the disclaimer of warranty and limitation of liability provided-above cannot be given local legal effect according to their terms,-reviewing courts shall apply local law that most closely approximates-an absolute waiver of all civil liability in connection with the-Program, unless a warranty or assumption of liability accompanies a-copy of the Program in return for a fee.--                     END OF TERMS AND CONDITIONS--            How to Apply These Terms to Your New Programs--  If you develop a new program, and you want it to be of the greatest-possible use to the public, the best way to achieve this is to make it-free software which everyone can redistribute and change under these terms.--  To do so, attach the following notices to the program.  It is safest-to attach them to the start of each source file to most effectively-state the exclusion of warranty; and each file should have at least-the "copyright" line and a pointer to where the full notice is found.--    <one line to give the program's name and a brief idea of what it does.>-    Copyright (C) <year>  <name of author>--    This program is free software: you can redistribute it and/or modify-    it under the terms of the GNU Affero General Public License as published by-    the Free Software Foundation, either version 3 of the License, or-    (at your option) any later version.--    This program is distributed in the hope that it will be useful,-    but WITHOUT ANY WARRANTY; without even the implied warranty of-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the-    GNU Affero General Public License for more details.--    You should have received a copy of the GNU Affero General Public License-    along with this program.  If not, see <http://www.gnu.org/licenses/>.--Also add information on how to contact you by electronic and paper mail.--  If your software can interact with users remotely through a computer-network, you should also make sure that it provides a way for users to-get its source.  For example, if your program is a web application, its-interface could display a "Source" link that leads users to an archive-of the code.  There are many ways you could offer source, and different-solutions will be better for different programs; see section 13 for the-specific requirements.--  You should also get your employer (if you work as a programmer) or school,-if any, to sign a "copyright disclaimer" for the program, if necessary.-For more information on this, and how to apply and follow the GNU AGPL, see-<http://www.gnu.org/licenses/>.
Makefile view
@@ -1,33 +1,30 @@-# Copyright (c) 2008--2011 Andres Loeh-# Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)-# This file is a part of the computer game Allure of the Stars-# and is released under the terms of the GNU Affero General Public License.-# For license and copyright information, see the file LICENSE.-#-+# some of this (not the parts run by CI) needs 'cabal install cabal-plan' play:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix play --dumpInitRngs+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix play --dumpInitRngs  shot:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix play --dumpInitRngs --printEachScreen+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix play --dumpInitRngs --printEachScreen  expose-lore:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode crawl --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode crawl --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000  dig-lore:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode dig --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode dig --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000  see-caves:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode see --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode see --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000 +short-caves:+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix know --newGame 5 --dumpInitRngs --gameMode short --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples --benchmark --noAnim --maxFps 1000+ configure-debug: 	cabal configure --enable-profiling --profiling-detail=all-functions -fwith_expensive_assertions --disable-optimization  configure-prof: 	cabal configure --enable-profiling --profiling-detail=exported-functions -ghcjs-new-build:-	cabal new-build -j1 --ghcjs --disable-library-profiling --disable-profiling .+ghcjs-build:+	cabal build --ghcjs .  chrome-log: 	google-chrome --enable-logging --v=1 file:///home/mikolaj/r/allureofthestars.github.io/play/index.html &@@ -36,228 +33,264 @@ 	google-chrome --no-sandbox --js-flags="--logfile=%t.log --prof" ../allureofthestars.github.io/play/index.html  minific:-	npx google-closure-compiler dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.9.5.0/x/Allure/build/Allure/Allure.jsexe/all.js --compilation_level=ADVANCED_OPTIMIZATIONS --isolation_mode=IIFE --assume_function_wrapper --externs=dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.9.5.0/x/Allure/build/Allure/Allure.jsexe/all.js.externs --jscomp_off="*" > ../allureofthestars.github.io/play/allure.all.js+	npx google-closure-compiler dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.2.0/x/Allure/build/Allure/Allure.jsexe/all.js --compilation_level=ADVANCED_OPTIMIZATIONS --isolation_mode=IIFE --assume_function_wrapper --externs=dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.2.0/x/Allure/build/Allure/Allure.jsexe/all.js.externs --externs=/home/mikolaj/r/lambdahack.github.io/lz-string.extern.js --jscomp_off="*" > ../allureofthestars.github.io/play/allure.all.js  minificForNode:-	npx google-closure-compiler dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.9.5.0/x/Allure/build/Allure/Allure.jsexe/all.js --compilation_level=ADVANCED_OPTIMIZATIONS --isolation_mode=IIFE --assume_function_wrapper --externs=dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/LambdaHack-0.9.5.0/x/LambdaHack/build/LambdaHack/LambdaHack.jsexe/all.js.externs --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/assert.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/child_process.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/crypto.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/dns.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/events.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/globals.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/https.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/os.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/punycode.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/readline.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/stream.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/tls.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/url.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/vm.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/buffer.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/cluster.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/dgram.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/domain.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/fs.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/http.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/net.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/path.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/querystring.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/repl.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/string_decoder.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/tty.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/util.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/zlib.js --jscomp_off="*" > ../allureofthestars.github.io/play/allure.all.js+	npx google-closure-compiler dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.2.0/x/Allure/build/Allure/Allure.jsexe/all.js --compilation_level=ADVANCED_OPTIMIZATIONS --isolation_mode=IIFE --assume_function_wrapper --externs=dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/Allure-0.10.2.0/x/Allure/build/Allure/Allure.jsexe/all.js.externs --externs=/home/mikolaj/r/lambdahack.github.io/lz-string.extern.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/assert.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/child_process.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/crypto.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/dns.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/events.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/globals.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/https.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/os.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/punycode.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/readline.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/stream.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/tls.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/url.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/vm.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/buffer.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/cluster.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/dgram.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/domain.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/fs.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/http.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/net.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/path.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/querystring.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/repl.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/string_decoder.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/tty.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/util.js --externs=/home/mikolaj/r/closure-compiler/contrib/nodejs/zlib.js --jscomp_off="*" > ../allureofthestars.github.io/play/allure.all.js  # Low delay to display animations swiftly and not bore the public too much. # Delay can't be lower than 2, because browsers sometimes treat delay 1 # specially and add their extra delay. create-gif : 	find ~/.Allure/screenshots/ -name 'prtscn*.bmp' -print0 | xargs -0 -r mogrify -format gif-	gifsicle -O3 --careful -d2 -l ~/.Allure/screenshots/prtscn*.gif -o ~/.Allure/screenshots/screenshot.gif+	../gifsicle/src/gifsicle -O3 --careful -d2 --colors 255 --no-extensions --no-conserve-memory -l ~/.Allure/screenshots/prtscn*.gif -o ~/.Allure/screenshots/screenshot.gif  frontendRaid:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode raid --exposeActors+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode raid --exposeActors  frontendBrawl:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode brawl+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 3 --dumpInitRngs --automateAll --gameMode brawl  frontendShootout:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode shootout+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode shootout  frontendHunt:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode hunt+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode hunt  frontendEscape:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 3 --dumpInitRngs --automateAll --gameMode escape+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 3 --dumpInitRngs --automateAll --gameMode escape  frontendZoo:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode zoo --exposeActors+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode zoo --exposeActors  frontendAmbush:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode ambush+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode ambush  frontendCrawl:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl --exposeItems --exposeActors+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl --exposeItems --exposeActors  frontendCrawlEmpty:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode "crawl empty"+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawlEmpty +frontendCrawlSurvival:+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode crawlSurvival --maxFps 1000+ frontendSafari:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 2 --dumpInitRngs --automateAll --gameMode safari --exposeActors+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode safari --exposeActors  frontendSafariSurvival:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode "safari survival" --exposeActors+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 5 --dumpInitRngs --automateAll --gameMode safariSurvival --exposeActors  frontendBattle:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 4 --dumpInitRngs --automateAll --gameMode battle --exposeActors+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 4 --dumpInitRngs --automateAll --gameMode battle --exposeActors  frontendBattleDefense:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 6 --dumpInitRngs --automateAll --gameMode "battle defense" --exposeActors+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 6 --dumpInitRngs --automateAll --gameMode battleDefense --exposeActors  frontendBattleSurvival:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 6 --dumpInitRngs --automateAll --gameMode "battle survival" --exposeActors+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 6 --dumpInitRngs --automateAll --gameMode battleSurvival --exposeActors  frontendDefense:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode defense --exposeItems --exposeActors+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode defense --exposeItems --exposeActors  frontendDefenseEmpty:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode "defense empty"+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 9 --dumpInitRngs --automateAll --gameMode defenseEmpty  fastCrawl:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl --exposeItems --exposeActors --showItemSamples --noAnim --maxFps 100000+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl --exposeItems --exposeActors --showItemSamples --noAnim --maxFps 100000 --benchmark +slowCrawl:+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --savePrefix test --newGame 1 --dumpInitRngs --automateAll --gameMode crawl --exposeItems --exposeActors --showItemSamples +# different benchmarks use different arguments+RNGOPTS=--setDungeonRng "SMGen 123 123" --setMainRng "SMGen 123 125"+RNGOPTS1=--setDungeonRng "SMGen 127 123" --setMainRng "SMGen 127 125"+RNGOPTS2=--setDungeonRng "SMGen 129 123" --setMainRng "SMGen 129 125"+ benchMemoryAnim:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --maxFps 100000 --benchmark --stopAfterFrames 33000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 120 --setMainRng 47 --frontendNull --noAnim +RTS -s -A1M -RTS+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --newGame 1 --maxFps 100000 --benchmark --stopAfterFrames 33000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS2)  --frontendNull --noAnim +RTS -s -A1M -RTS  benchBattle:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7+	./Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1) +benchBattle-appveyor:+	./Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1500 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1)+ benchAnimBattle:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 3 --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7+	./Allure --dbgMsgSer --logPriority 4 --newGame 3 --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1)  benchFrontendBattle:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7+	./Allure --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS1)  benchCrawl:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0+	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)  benchFrontendCrawl:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0+	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --benchmark --stopAfterFrames 7000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)  benchDig:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1 --automateAll --keepAutomated --gameMode dig --setDungeonRng 0 --setMainRng 0+	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1 --automateAll --keepAutomated --gameMode dig $(RNGOPTS)  benchNull: benchBattle benchAnimBattle benchCrawl  bench: benchBattle benchAnimBattle benchFrontendBattle benchCrawl benchFrontendCrawl  nativeBenchCrawl:-	dist/build/Allure/Allure		   --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS) +nativeBenchCrawl-appveyor:+	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)+ nativeBenchBattle:-	dist/build/Allure/Allure		   --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 0 --setMainRng 0+	$$(cabal-plan list-bin Allure) --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS)  nativeBench: nativeBenchBattle nativeBenchCrawl  nodeBenchCrawl:-	node dist/build/Allure/Allure.jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0+	node $$(cabal-plan list-bin Allure).jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS)  nodeBenchBattle:-	node dist/build/Allure/Allure.jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle --setDungeonRng 0 --setMainRng 0+	node $$(cabal-plan list-bin Allure).jsexe/all.js --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS)  nodeBench: nodeBenchBattle nodeBenchCrawl +nodeMinifiedBench:+	node ../allureofthestars.github.io/play/allure.all.js --dbgMsgSer --logPriority 4 --newGame 3 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 1000 --automateAll --keepAutomated --gameMode battle $(RNGOPTS)+	node ../allureofthestars.github.io/play/allure.all.js --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 2000 --automateAll --keepAutomated --gameMode crawl $(RNGOPTS) -test-travis: test-sniff test-short test-medium benchNull +test-travis: test-short test-medium++test-travis2: test-sniff benchNull+ test: test-sniff test-short test-medium benchNull  test-short: test-short-new test-short-load -test-medium: testRaid-medium testBrawl-medium testShootout-medium testHunt-medium testEscape-medium testZoo-medium testAmbush-medium testCrawl-medium testCrawlEmpty-medium testCrawl-medium-know testSafari-medium testSafariSurvival-medium testBattle-medium testBattleDefense-medium testBattleSurvival-medium testDig-medium testDefenseEmpty-medium+test-medium: testRaid-medium testBrawl-medium testShootout-medium testHunt-medium testEscape-medium testZoo-medium testAmbush-medium testCrawlEmpty-medium testCrawl-medium-know testSafari-medium testSafariSurvival-medium testBattle-medium testBattleDefense-medium testBattleSurvival-medium testDig-medium testDefenseEmpty-medium testMany-teletype  test-sniff:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterFrames 1  --dumpInitRngs --automateAll --keepAutomated --gameMode raid --sniff 2> /tmp/teletypetest.log+	bash -c "./Allure --dbgMsgSer --logPriority 4 --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterFrames 1  --dumpInitRngs --automateAll --keepAutomated --gameMode raid --sniff &> /tmp/teletypetest.log" +testMany-teletype:+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 9 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 50 --dumpInitRngs --automateAll --keepAutomated 2> /tmp/teletypetest.log++testMany-sdlInit:+	./Allure --dbgMsgSer --logPriority 0 --boostRandomItem --newGame 9 --maxFps 100000 --benchmark --stopAfterSeconds 50 --dumpInitRngs --automateAll --keepAutomated 2> /tmp/teletypetest.log+ testRaid-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode raid 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode raid 2> /tmp/teletypetest.log  testBrawl-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode brawl 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode brawl 2> /tmp/teletypetest.log  testShootout-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode shootout 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode shootout 2> /tmp/teletypetest.log  testHunt-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode hunt 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode hunt 2> /tmp/teletypetest.log  testEscape-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode escape 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode escape 2> /tmp/teletypetest.log  testZoo-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 2 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode zoo 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 2 --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode zoo 2> /tmp/teletypetest.log  testAmbush-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode ambush 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode ambush 2> /tmp/teletypetest.log  testCrawl-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 200 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterGameOver --stopAfterSeconds 350 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --assertExplored 4 2> /tmp/teletypetest.log +testCrawl-medium-double:+	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendLazy --benchmark --stopAfterGameOver --stopAfterSeconds 590 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --assertExplored 4+ testCrawlEmpty-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode "crawl empty" 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode crawlEmpty 2> /tmp/teletypetest.log  testCrawl-medium-know:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix know --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix know --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --knowItems --exposePlaces --exposeItems --exposeActors --showItemSamples 2> /tmp/teletypetest.log  testSafari-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 2 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode safari 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode safari 2> /tmp/teletypetest.log  testSafariSurvival-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 8 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode safariSurvival 2> /tmp/teletypetest.log  testBattle-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode battle 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 3 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 20 --dumpInitRngs --automateAll --keepAutomated --gameMode battle 2> /tmp/teletypetest.log  testBattleDefense-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 7 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode "battle defense" 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 7 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode battleDefense 2> /tmp/teletypetest.log  testBattleSurvival-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 7 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 7 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode battleSurvival 2> /tmp/teletypetest.log  testDefense-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 9 --noAnim --maxFps 100000 --frontendLazy --benchmark --stopAfterSeconds 200 --dumpInitRngs --automateAll --keepAutomated --gameMode defense 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --newGame 9 --noAnim --maxFps 100000 --frontendLazy --benchmark --stopAfterSeconds 100 --dumpInitRngs --automateAll --keepAutomated --gameMode defense 2> /tmp/teletypetest.log  testDig-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterFrames 100 --dumpInitRngs --automateAll --keepAutomated --gameMode dig 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterFrames 100 --dumpInitRngs --automateAll --keepAutomated --gameMode dig 2> /tmp/teletypetest.log  testDefenseEmpty-medium:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 9 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode "defense empty" 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 9 --noAnim --maxFps 100000 --frontendTeletype --benchmark --stopAfterSeconds 40 --dumpInitRngs --automateAll --keepAutomated --gameMode defenseEmpty 2> /tmp/teletypetest.log  test-short-new:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootout --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix hunt --dumpInitRngs --automateAll --keepAutomated --gameMode hunt --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battle --showItemSamples --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battleDefense --dumpInitRngs --automateAll --keepAutomated --gameMode "battle defense" --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootout --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix hunt --dumpInitRngs --automateAll --keepAutomated --gameMode hunt --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode safariSurvival --showItemSamples --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battle --showItemSamples --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battleDefense --dumpInitRngs --automateAll --keepAutomated --gameMode battleDefense --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --newGame 5 --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode battleSurvival --frontendTeletype --stopAfterSeconds 2 2> /tmp/teletypetest.log -# "--setDungeonRng 0 --setMainRng 0" is needed for determinism relative to seed+# $(RNGOPTS) is needed for determinism relative to seed # generated before game save test-short-load:-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootout --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix hunt --dumpInitRngs --automateAll --keepAutomated --gameMode hunt --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "safari survival" --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battleDefense --dumpInitRngs --automateAll --keepAutomated --gameMode "battle defense" --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log-	dist/build/Allure/Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode "battle survival" --frontendTeletype --stopAfterSeconds 2 --setDungeonRng 0 --setMainRng 0 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix raid --dumpInitRngs --automateAll --keepAutomated --gameMode raid --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix brawl --dumpInitRngs --automateAll --keepAutomated --gameMode brawl --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix shootout --dumpInitRngs --automateAll --keepAutomated --gameMode shootout --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix hunt --dumpInitRngs --automateAll --keepAutomated --gameMode hunt --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix escape --dumpInitRngs --automateAll --keepAutomated --gameMode escape --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix zoo --dumpInitRngs --automateAll --keepAutomated --gameMode zoo --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix ambush --dumpInitRngs --automateAll --keepAutomated --gameMode ambush --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix crawl --dumpInitRngs --automateAll --keepAutomated --gameMode crawl --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safari --dumpInitRngs --automateAll --keepAutomated --gameMode safari --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix safariSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode safariSurvival --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battle --dumpInitRngs --automateAll --keepAutomated --gameMode battle --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battleDefense --dumpInitRngs --automateAll --keepAutomated --gameMode battleDefense --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log+	./Allure --dbgMsgSer --logPriority 4 --boostRandomItem --savePrefix battleSurvival --dumpInitRngs --automateAll --keepAutomated --gameMode battleSurvival --frontendTeletype --stopAfterSeconds 2 $(RNGOPTS) 2> /tmp/teletypetest.log  -version:-	dist/build/Allure/Allure --version--build-binary-common:-	cabal v1-install --disable-library-profiling --disable-profiling --disable-documentation --enable-optimization --only-dependencies-	cabal v1-configure --disable-library-profiling --disable-profiling --enable-optimization --prefix=/ --datadir=. --datasubdir=.+build-binary-v1:+	cabal v1-install --force-reinstalls --disable-library-profiling --disable-profiling --disable-documentation --enable-optimization --only-dependencies+	cabal v1-configure --disable-library-profiling --disable-profiling --enable-optimization 	cabal v1-build exe:Allure-	mkdir -p AllureOfTheStars/GameDefinition/fonts++copy-binary-v1: 	cabal v1-copy --destdir=AllureOfTheStarsInstall-	cp GameDefinition/config.ui.default AllureOfTheStars/GameDefinition-	cp GameDefinition/fonts/16x16xw.woff AllureOfTheStars/GameDefinition/fonts-	cp GameDefinition/fonts/16x16xw.bdf AllureOfTheStars/GameDefinition/fonts-	cp GameDefinition/fonts/16x16x.fnt AllureOfTheStars/GameDefinition/fonts-	cp GameDefinition/fonts/8x8xb.fnt AllureOfTheStars/GameDefinition/fonts-	cp GameDefinition/fonts/8x8x.fnt AllureOfTheStars/GameDefinition/fonts-	cp GameDefinition/fonts/LICENSE.16x16x AllureOfTheStars/GameDefinition/fonts-	cp GameDefinition/fonts/Fix15Mono-Bold.woff AllureOfTheStars/GameDefinition/fonts-	cp GameDefinition/fonts/LICENSE.Fix15Mono-Bold AllureOfTheStars/GameDefinition/fonts++copy-binary:+	cp $$(cabal-plan list-bin Allure) AllureOfTheStars++configure-binary-v2:+	cabal configure --project-file=cabal.project.LH.dir --disable-tests --disable-library-profiling --disable-profiling --enable-optimization++configure-binary-v2-vty:+	cabal configure -fvty --project-file=cabal.project.LH.dir --disable-tests --disable-library-profiling --disable-profiling --disable-documentation --enable-optimization++build-binary-v2:+	cabal build --project-file=cabal.project.LH.dir --only-dependencies .+	cabal build --project-file=cabal.project.LH.dir exe:Allure++copy-directory:+	mkdir -p AllureOfTheStars/GameDefinition 	cp GameDefinition/InGameHelp.txt AllureOfTheStars/GameDefinition 	cp GameDefinition/PLAYING.md AllureOfTheStars/GameDefinition 	cp README.md AllureOfTheStars@@ -266,28 +299,26 @@ 	cp COPYLEFT AllureOfTheStars 	cp CREDITS AllureOfTheStars -#in LambdaHack/-# cabal v1-install --disable-library-profiling --disable-profiling --disable-documentation --enable-optimization-build-binary-ubuntu: build-binary-common-	cp AllureOfTheStarsInstall/bin/Allure AllureOfTheStars-	dist/build/Allure/Allure --version > /dev/null; \+build-binary-common: build-binary-v1 copy-directory copy-binary-v1++build-binary-windows: configure-binary-v2 build-binary-v2 copy-directory++build-directory: configure-binary-v2 build-binary-v2 copy-directory copy-binary++build-binary-ubuntu: build-directory+	AllureOfTheStars/Allure --version > /dev/null; \ 	LH_VERSION=$$(cat ~/.Allure/stdout.txt); \ 	tar -czf Allure_$${LH_VERSION}_ubuntu-16.04-amd64.tar.gz AllureOfTheStars -build-binary-macosx: build-binary-common-	cp AllureOfTheStarsInstall/bin/Allure AllureOfTheStars-	dist/build/Allure/Allure --version > /dev/null; \+build-binary-macosx: build-directory+	AllureOfTheStars/Allure --version > /dev/null; \ 	LH_VERSION=$$(cat ~/.Allure/stdout.txt); \ 	OS_VERSION=$$(sw_vers -productVersion); \ 	tar -czf Allure_$${LH_VERSION}_macosx-$${OS_VERSION}-amd64.tar.gz AllureOfTheStars -#in LambdaHack/-# cabal v1-install --disable-library-profiling --disable-profiling --disable-documentation --enable-optimization -fvty-build-binary-screen-reader-ubuntu: build-binary-common-	cp AllureOfTheStarsInstall/bin/Allure AllureOfTheStars-	dist/build/Allure/Allure --version > /dev/null; \+build-directory-vty: configure-binary-v2-vty build-binary-v2 copy-directory copy-binary++build-binary-screen-reader-ubuntu: build-directory-vty+	AllureOfTheStars/Allure --version > /dev/null; \ 	LH_VERSION=$$(cat ~/.Allure/stdout.txt); \ 	tar -czf Allure_$${LH_VERSION}_screen-reader-ubuntu-16.04-amd64.tar.gz AllureOfTheStars--new-build-dev:-	cabal new-build --datadir=. --disable-optimization -j1 all
README.md view
@@ -1,21 +1,19 @@ Allure of the Stars =================== -[![Build Status](https://travis-ci.org/AllureOfTheStars/Allure.svg?branch=master)](https://travis-ci.org/AllureOfTheStars/Allure)+[![Build Status](https://secure.travis-ci.org/AllureOfTheStars/Allure.svg?master)](https://travis-ci.org/AllureOfTheStars/Allure) [![Hackage](https://img.shields.io/hackage/v/Allure.svg)](https://hackage.haskell.org/package/Allure)-[![Join the chat at https://gitter.im/AllureOfTheStars/Allure](https://badges.gitter.im/AllureOfTheStars/Allure.svg)](https://gitter.im/AllureOfTheStars/Allure?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)+[![Join the chat at Discord](https://img.shields.io/discord/688792755564052486.svg?label=chat%20on%20Discord&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/87Ghnws)+[![Join the chat at Matrix](https://img.shields.io/matrix/lambdahack:mozilla.org.svg?label=chat%20on%20Matrix&logo=matrix&server_fqdn=mozilla.modular.im)](https://matrix.to/#/!HnbpAHMjOGHlYtrASl:mozilla.org?via=mozilla.modular.im)  Allure of the Stars is a near-future Sci-Fi roguelike[2] and tactical squad combat game. Binaries and the game manual-are available at the homepage[6]. You can try the game out+are available at the homepage[6]. You can also try the game out in the browser at http://allureofthestars.com/play.-(It runs fastest on Chrome. Keyboard commands and savefiles-are supported only on recent enough versions of browsers.-Mouse should work everywhere.) -![gameplay screenshot](https://raw.githubusercontent.com/AllureOfTheStars/media/master/screenshot/allureofthestars.com.updown.png)+![gameplay screenshot](https://raw.githubusercontent.com/AllureOfTheStars/media/master/screenshot/allureofthestars.com.second.attempt.gif) -Not a single picture in this game. You have to imagine everything+Not a single image in this game. You have to imagine everything yourself, like with a book (a grown-up book, without pictures). Once you learn to imagine things, though, you can keep exploring and mastering the world and making up stories for a long time.@@ -28,14 +26,14 @@ content generation and persistent content modification based on player behaviour. Contributions are welcome. Please offer feedback to mikolaj.konarski@funktory.com or, preferably,-at any of the public forums.+on any of the public forums.   Game installation from binary archives -------------------------------------- -The game runs rather slowly in the browser (fastest on Chrome)-and you are limited to only one font, though it's scalable.+The game runs rather slowly in the browser (fastest on Chrome) and you are+limited to the square font for all purposes, though it's scalable. Also, savefiles are prone to corruption on the browser, e.g., when it's closed while the game is still saving progress (which takes a long time). Hence, after trying out the game,@@ -48,7 +46,7 @@ if available. On Linux, make sure you have the SDL2 libraries installed on your system (e.g., libsdl2-2.0-0 and libsdl2-ttf-2.0-0 on Ubuntu). For Windows (XP no longer supported), the SDL2 and all other needed libraries-are already contained in the game's binary archive.+are included in the game's binary archive.  Max OS X binaries for the few most popular OS versions are accessible from Homebrew via `brew install allureofthestars`,@@ -60,77 +58,114 @@  The game UI can be configured via a config file. The default settings, the same that are built into the binary,-are in [GameDefinition/config.ui.default](https://github.com/AllureOfTheStars/Allure/blob/master/GameDefinition/config.ui.default).-When the game is run for the first time, the file is copied to the default-user data folder, which is `~/.Allure/` on Linux,+are on github at [GameDefinition/config.ui.default](https://github.com/AllureOfTheStars/Allure/blob/master/GameDefinition/config.ui.default).+When the game is run for the first time, or whenever the settings file+is deleted, the file is written to the default user data location,+which is `~/.Allure/` on Linux, `C:\Users\<username>\AppData\Roaming\Allure\` (or `C:\Documents And Settings\user\Application Data\Allure\`-or something else altogether) on Windows, and in RMB menu, under-`Inspect/Application/Local Storage` when run inside the Chrome browser.+or something else altogether) on Windows+and `Inspect/Application/Local Storage` under RMB menu+when run inside the Chrome browser.+If the user config file is outdated or corrupted, it's automatically+moved away together with old savefiles, which guarantees that the new+default config file is ultimately put in its place. -Screen font and consequently window size can be changed by editing+Screen fonts and, consequently, window size can be changed by editing the config file in the user data folder. The default bitmap font-`16x16xw.bdf` covers most national characters in the Latin alphabet-(e.g. to give custom names to player characters) and results-in a game window of exactly 720p (standard HD) dimensions. The `8x8xb.fnt`+`16x16xw.bdf` used for the game map covers most national characters+in the Latin alphabet (e.g. to give custom names to player characters)+and results in a game window of exactly 720p HD dimensions. The `8x8xb.fnt` bitmap font results in a tiny window and covers latin-1 characters only.-The scalable `16x16xw.woff` font results in window sizes dependent-on the `scalableFontSize` parameter in the config file.-With `scalableFontSize = 16` it should look almost the same-as the pixel-perfect `16x16xw.bdf`.+The config file parameter `allFontsScale` permits further window size+adjustments, automatically switching to the scalable `16x16xw.woff`+version of the game map font. Config file option `chosenFontset` governs+not only the main game map font, but also the shape of the rectangular fonts,+if any, in which longer texts are overlaid over the map. -If you don't have a numeric keypad, you can use the Vi editor keys-(aka roguelike keys) or mouse for movement or you can enable the compact-laptop keys (uk8o79jl) in the config file. If numeric keypad doesn't work,-toggling the Num Lock key sometimes helps. If running with the Shift key-and keypad keys doesn't work, try Control key instead.-The game is fully playable with mouse only, as well as with keyboard only,-but the most efficient combination for some players is mouse for go-to,-inspecting, and aiming at distant positions and keyboard for everything else.+For high resolution displays and/or if fullscreen mode is requested+in the configuration file, `allFontsScale` needs to be set.+E.g., scale 3 works for 4K displays. Otherwise, the letters may be+too small or, in fullscreen or on retina displays in OS X,+the screen may be automatically scaled as a whole, not each letter+separately, softening letter edges of the square fonts that should+be pixel-perfect and crisp. +If you don't have a numeric keypad, you can use the left-hand movement+key setup (axwdqezc) or Vi editor keys (aka roguelike keys) or mouse.+If numeric keypad doesn't work, toggling the Num Lock key sometimes helps.+If running with the Shift key and keypad keys doesn't work,+try the Control key instead. The game is fully playable with mouse only,+as well as with keyboard only, but the most efficient combination+may be mouse for menus, go-to, inspecting the map, aiming at distant+positions and keyboard for everything else.+ If you are using a terminal frontend, e.g. the best supported vty frontend,-numeric keypad (e.g., keypad '*' and '/') may not work correctly-depending on versions of the libraries, terminfo and terminal emulators.-Toggling the Num Lock key may help or make issues worse. As a workaround-for the vty frontend, numbers are used for movement, which sadly prevents+then numeric keypad (especially keypad `*` and `/`) may not work correctly,+depending on versions of libraries, terminfo and terminal emulators.+Toggling the Num Lock key may help or make issues worse. As a workaround,+in the vty frontend, numbers are used for movement, which sadly prevents the number keys from selecting heroes. The commands that require pressing Control and Shift together won't work either, but fortunately they are not crucial to gameplay. -Some effort has been put to help using the vty frontend with screen readers,-but without feedback it's hard to say how accesible that setup is.+Some effort went into making the vty frontend usable with screen readers,+but without feedback it's hard to say how accessible that setup is. As a side effect of screen reader support, there is no aiming line-nor path in vty frontend. Screen readers may also work better with animations-turned off using `--noAnim` or the corresponding config file option.-Note that unicode and cursor support are now necessary for correct output.+nor path in vty frontend and some of map position highlighting+is performed using the terminal cursor. Screen readers may also work+better with animations turned off, using `--noAnim` or the corresponding+config file option.  -Compilation from source--------------------------If you want to compile native binaries from the source code,-use Cabal (already a part of your OS distribution, or available within-The Haskell Platform[7]), which also takes care of all the dependencies.+Compiling native binary from source+-----------------------------------  The recommended frontend is based on SDL2, so you need the SDL2 libraries for your OS. On Linux, remember to install the -dev versions as well, e.g., libsdl2-dev and libsdl2-ttf-dev on Ubuntu Linux 16.04.-(Compilation to JavaScript for the browser is more complicated-and requires the ghcjs[15] compiler and optionally the Google Closure-Compiler[16] as well.)+Other frontends are compiled similarly, but compilation to JavaScript+for the browser is more complicated and requires the ghcjs[15] compiler+and optionally the Google Closure Compiler[16].  The latest official version of the game can be downloaded,-compiled for SDL2 and installed automatically by Cabal from Hackage[3]-as follows+compiled for SDL2 and installed automatically using the Cabal tool,+which is already a part of your OS distribution, or available within+The Haskell Platform[7]. Get the game from Hackage[3] as follows      cabal update-    cabal install Allure+    cabal run Allure -For a newer version, install a matching LambdaHack library snapshot,-clone the game source from github[5] and run `cabal install`-from the main directory.+For a newer, unofficial version, clone the game source from github[5],+clone a matching LambdaHack library snapshot into ../LambdaHack+and run `cabal run` from the main directory.+Alternatively, if you'd like to develop in this codebase,+the following speeds up the turn-around a lot +    cp cabal.project.local.development cabal.project.local+    cabal install cabal-plan +and then compile with++    cabal build .++and run the game with++    make play++There is a built-in black and white line terminal frontend, suitable+for teletype terminals or a keyboard and a printer (but it's going to use+a lot of paper, unless you disable animations with `--noAnim`). It is used+in CI and for some tests and benchmarks defined in Makefile. To compile+with one of the less rudimentary terminal frontends (in which case you are+on your own regarding font choice and color setup and you won't have+the spiffy colorful squares outlining special positions that exist in SDL2+frontend, but only crude cursor highlights), use Cabal flags, e.g,+to switch to the vty console frontend optimized for screen readers, run++    cabal run -fvty Allure++ Testing and debugging --------------------- @@ -139,8 +174,8 @@ Numerous tests that use the screensaver game modes (AI vs. AI) and the teletype frontend are gathered in `make test`. Of these, travis runs `test-travis` on each push to github.-Test commands with prefix `frontend` start AI vs. AI games-with the standard, user-friendly frontend.+Test commands with prefix `frontend` start AI vs. AI games with+the standard, user-friendly frontend and auto-locating the game binary.  Run `Allure --help` to see a brief description of all debug options. Of these, the `--sniff` option is very useful (though verbose@@ -162,14 +197,24 @@ you are editing. Put big formatting changes in separate commits.  Haddocks are provided for all module headers and for all functions and types-from major modules, in particular the modules that are interfaces+from major modules, in particular for the modules that are interfaces for a whole directory of modules. Apart of that, only very important functions and types are distinguished by having a haddock. If minor ones have comments, they should not be haddocks and they are permitted to describe implementation details and be out of date.-Prefer assertions to comments, unless too verbose.+Prefer assertions instead of comments, unless too verbose. +The 'pointman' from game manual and UI is called 'leader' in the source code+and there are a few more mismatches, though the source code naming+and the UI naming should each be consistent in separation.+If the UI names stick, perhaps source code will be renamed at some point. +This codebase is an experiment in extensive use of states without lens.+So far, it works, doesn't result in much larger files or lots+of repetition and has the added benefits that newcomers don't need+to learn any lens library. Record punning, etc., definitely helps.++ Further information ------------------- @@ -186,7 +231,7 @@  Copyright (c) 2008--2011 Andres Loeh -Copyright (c) 2010--2019 Mikolaj Konarski and others (see git history)+Copyright (c) 2010--2021 Mikolaj Konarski and others (see git history)  Allure of the Stars is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by
+ test/SessionUIMock.hs view
@@ -0,0 +1,148 @@+module SessionUIMock+  ( unwindMacros, unwindMacrosAcc+  ) where++import Prelude ()++import Game.LambdaHack.Core.Prelude++import           Control.Monad.Trans.Class+import           Control.Monad.Trans.State.Lazy+import           Control.Monad.Trans.Writer.Lazy+import           Data.Bifunctor (bimap)+import qualified Data.Map.Strict as M++import qualified Game.LambdaHack.Client.UI.Content.Input as IC+import           Game.LambdaHack.Client.UI.ContentClientUI+import           Game.LambdaHack.Client.UI.FrameM+import           Game.LambdaHack.Client.UI.HandleHumanLocalM+import           Game.LambdaHack.Client.UI.HandleHumanM+import qualified Game.LambdaHack.Client.UI.HumanCmd as HumanCmd+import qualified Game.LambdaHack.Client.UI.Key as K+import           Game.LambdaHack.Client.UI.SessionUI+  (KeyMacro (..), KeyMacroFrame (..), emptyMacroFrame)++data SessionUIMock = SessionUIMock+  { smacroFrame :: KeyMacroFrame+  , smacroStack :: [KeyMacroFrame]+  , sccui       :: CCUI+  , unwindTicks :: Int+  }++type KeyMacroBufferMock = Either String String+type KeyPendingMock = String+type KeyLastMock = String++type BufferTrace = [(KeyMacroBufferMock, KeyPendingMock, KeyLastMock)]+type ActionLog = String++data Op = Looped | HeadEmpty++humanCommandMock :: WriterT [(BufferTrace, ActionLog)] (State SessionUIMock) ()+humanCommandMock = do+  abuffs <- lift $ do+    sess <- get+    return $ renderTrace (smacroFrame sess : smacroStack sess)  -- log session+  abortOrCmd <- lift iterationMock -- do stuff+  -- GC macro stack if there are no actions left to handle,+  -- removing all unnecessary macro frames at once,+  -- but leaving the last one for user's in-game macros.+  lift $ modify $ \sess ->+          let (smacroFrameNew, smacroStackMew) =+                dropEmptyMacroFrames (smacroFrame sess) (smacroStack sess)+          in sess { smacroFrame = smacroFrameNew+                  , smacroStack = smacroStackMew }+  case abortOrCmd of+    Left Looped -> tell [(abuffs, "Macro looped")] >> pure ()+    Left HeadEmpty -> tell [(abuffs, "")] >> pure ()  -- exit loop+    Right Nothing -> tell [(abuffs, "")] >> humanCommandMock+    Right (Just out) -> tell [(abuffs, show out)] >> humanCommandMock++iterationMock :: State SessionUIMock (Either Op (Maybe K.KM))+iterationMock = do+  SessionUIMock _ _ CCUI{coinput=IC.InputContent{bcmdMap}} ticks <- get+  if ticks <= 1000+  then do+    modify $ \sess -> sess {unwindTicks = ticks + 1}+    mkm <- promptGetKeyMock+    case mkm of+      Nothing -> return $ Left HeadEmpty  -- macro finished+      Just km -> case km `M.lookup` bcmdMap of+        Just (_, _, cmd) -> Right <$> cmdSemInCxtOfKMMock km cmd+        _ -> return $ Right $ Just km  -- unknown command; fine for tests+  else return $ Left Looped++cmdSemInCxtOfKMMock :: K.KM -> HumanCmd.HumanCmd+                    -> State SessionUIMock (Maybe K.KM)+cmdSemInCxtOfKMMock km cmd = do+  modify $ \sess ->+    sess {smacroFrame = updateKeyLast km cmd $ smacroFrame sess}+  cmdSemanticsMock km cmd++cmdSemanticsMock :: K.KM -> HumanCmd.HumanCmd+                 -> State SessionUIMock (Maybe K.KM)+cmdSemanticsMock km = \case+  HumanCmd.Macro s -> do+    modify $ \sess ->+      let kms = K.mkKM <$> s+          (smacroFrameNew, smacroStackMew) =+             macroHumanTransition kms (smacroFrame sess) (smacroStack sess)+      in sess { smacroFrame = smacroFrameNew+              , smacroStack = smacroStackMew }+    return Nothing+  HumanCmd.Repeat n -> do+    modify $ \sess ->+      let (smacroFrameNew, smacroStackMew) =+             repeatHumanTransition n (smacroFrame sess) (smacroStack sess)+      in sess { smacroFrame = smacroFrameNew+              , smacroStack = smacroStackMew }+    return Nothing+  HumanCmd.RepeatLast n -> do+    modify $ \sess ->+      sess {smacroFrame = repeatLastHumanTransition n (smacroFrame sess) }+    return Nothing+  HumanCmd.Record -> do+    modify $ \sess ->+      sess {smacroFrame = fst $ recordHumanTransition (smacroFrame sess) }+    return Nothing+  _ -> return $ Just km++promptGetKeyMock :: State SessionUIMock (Maybe K.KM)+promptGetKeyMock = do+  SessionUIMock macroFrame _ CCUI{coinput=IC.InputContent{bcmdMap}} _ <- get+  case keyPending macroFrame of+    KeyMacro (km : kms) -> do+        modify $ \sess ->+          sess {smacroFrame = (smacroFrame sess) {keyPending = KeyMacro kms}}+        modify $ \sess ->+          sess {smacroFrame = addToMacro bcmdMap km $ smacroFrame sess}+        return (Just km)+    KeyMacro [] -> return Nothing++unwindMacros :: IC.InputContent -> KeyMacro -> [(BufferTrace, ActionLog)]+unwindMacros coinput keyPending =+  let initSession = SessionUIMock+        { smacroFrame = emptyMacroFrame {keyPending}+        , smacroStack = []+        , sccui = emptyCCUI {coinput}+        , unwindTicks = 0 }+  in evalState (execWriterT humanCommandMock) initSession++accumulateActions :: [(BufferTrace, ActionLog)] -> [(BufferTrace, ActionLog)]+accumulateActions ba =+  let (buffers, actions) = unzip ba+      actionlog = concat <$> inits actions+  in zip buffers actionlog++unwindMacrosAcc :: IC.InputContent -> KeyMacro -> [(BufferTrace, ActionLog)]+unwindMacrosAcc coinput keyPending =+  accumulateActions $ unwindMacros coinput keyPending++renderTrace :: [KeyMacroFrame] -> BufferTrace+renderTrace macroFrames =+  let buffers = bimap (concatMap K.showKM)+                      (concatMap K.showKM . unKeyMacro)+                . keyMacroBuffer <$> macroFrames+      pendingKeys = concatMap K.showKM . unKeyMacro . keyPending <$> macroFrames+      lastKeys = maybe "" K.showKM . keyLast <$> macroFrames+  in zip3 buffers pendingKeys lastKeys
+ test/Spec.hs view
@@ -0,0 +1,326 @@+{-# LANGUAGE PackageImports #-}+module Main (main) where++import Prelude ()++import Game.LambdaHack.Core.Prelude++import qualified Data.Map.Strict as M+import qualified Data.Text as T+import           Options.Applicative+import           System.IO.Unsafe (unsafePerformIO)+import           Test.Tasty+import           Test.Tasty.HUnit++import qualified Game.LambdaHack.Client.UI.Content.Input as IC+import           Game.LambdaHack.Client.UI.Frontend.Chosen+import qualified Game.LambdaHack.Client.UI.HumanCmd as HumanCmd+import qualified Game.LambdaHack.Client.UI.Key as K+import           Game.LambdaHack.Client.UI.SessionUI+import           Game.LambdaHack.Client.UI.UIOptions+import           Game.LambdaHack.Client.UI.UIOptionsParse+import           Game.LambdaHack.Common.ClientOptions+import qualified Game.LambdaHack.Content.RuleKind as RK+import           Game.LambdaHack.Server++-- For the case of flattened .cabal file, to avoid ambiguity.+import qualified "Allure" Client.UI.Content.Input as Content.Input+import qualified "Allure" Content.RuleKind+import           "Allure" TieKnot++import SessionUIMock++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "Tests" [macroTests, integrationTests]++macroTests :: TestTree+macroTests = testGroup "macroTests" $+  let coinput = IC.makeData Nothing Content.Input.standardKeysAndMouse+      stringToKeyMacro = KeyMacro . map (K.mkKM . (: []))+      listToKeyMacro = KeyMacro . map K.mkKM+      bindInput l input =+        let ltriple = M.fromList $ map (\(k, ks) ->+              (K.mkKM k, ([], "", HumanCmd.Macro $ map (: []) ks))) l+        in input {IC.bcmdMap = M.union ltriple $ IC.bcmdMap input}+  in [ testCase "Macro 1 from PR#192 description" $+         fst <$> unwindMacros coinput (stringToKeyMacro "'j''j'")+         @?= [ [ (Right "",     "'j''j'",  "")  ]+             , [ (Left  "",     "j''j'",   "") ]+             , [ (Left  "j",    "''j'",    "j") ]+             , [ (Right "j",    "'j'",     "j") ]+             , [ (Left  "",     "j'",      "j") ]+             , [ (Left  "j",    "'",       "j") ]+             , [ (Right "j",    "",        "j") ]+             ]+     , testCase "Macro 1 from Issue#189 description" $+         snd (last (unwindMacros (bindInput [ ("a", "'bc'V")+                                            , ("c", "'aaa'V") ] coinput)+                                 (stringToKeyMacro "a")))+         @?= "Macro looped"+     , testCase "Macro 2 from Issue#189 description" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'x'")] coinput)+                                    (stringToKeyMacro "'a'")))+         @?= "x"+     , testCase "Macro 3 from Issue#189 description" $+         snd (last (unwindMacrosAcc coinput+                                    (stringToKeyMacro "'x''x'")))+         @?= "xx"+     , testCase "Macro 4 from Issue#189 description" $+         snd (last (unwindMacrosAcc coinput+                                    (stringToKeyMacro "'x''x'V")))+         @?= "xxx"+     , testCase "Macro 5 from Issue#189 description" $+         snd (last (unwindMacrosAcc coinput+                                    (stringToKeyMacro "x'x'V")))+         @?= "xxx"+     , testCase "Macro test 10" $+         snd (last (unwindMacrosAcc coinput+                                    (stringToKeyMacro "x'y'V")))+         @?= "xyy"+     , testCase "Macro test 11" $+         snd (last (unwindMacrosAcc coinput+                                    (stringToKeyMacro "'x''y'V")))+         @?= "xyy"+     , testCase "Macro test 12" $+         snd (last (unwindMacrosAcc coinput+                                    (listToKeyMacro ["x", "C-V"])))+         @?= "x"+     , testCase "Macro test 13" $+         snd (last (unwindMacrosAcc coinput+                                    (listToKeyMacro ["'", "x", "'", "C-V"])))+         @?= "xxxxxxxxxxxxxxxxxxxxxxxxxx"+     , testCase "Macro test 14" $+         snd (last (unwindMacrosAcc coinput+                                    (listToKeyMacro+                                       ["'", "x", "'", "y", "C-V"])))+         @?= "xyxxxxxxxxxxxxxxxxxxxxxxxxx"+     , testCase "Macro test 15" $+         snd (last (unwindMacrosAcc (bindInput [("a", "x")] coinput)+                                    (stringToKeyMacro "'a'V")))+         @?= "xx"+     , testCase "Macro test 16" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'x'")] coinput)+                                    (stringToKeyMacro "'a'V")))+         @?= "xx"+     , testCase "Macro test 17" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'x'V")] coinput)+                                    (stringToKeyMacro "a")))+         @?= "xx"+     , testCase "Macro test 18" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'x'V")] coinput)+                                    (stringToKeyMacro "'a'")))+         @?= "xx"+     , testCase "Macro test 19" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'x'V")] coinput)+                                    (stringToKeyMacro "'a'V")))+         @?= "xxxx"+     , testCase "Macro test 20" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'bz'V")+                                               , ("c", "'aaa'V") ] coinput)+                                    (stringToKeyMacro "c")))+         @?= "bzbzbzbzbzbzbzbzbzbzbzbz"+     , testCase "RepeatLast test 10" $+         snd (last (unwindMacrosAcc coinput+                                    (stringToKeyMacro "x'y'v")))+         @?= "xyy"+     , testCase "RepeatLast test 11" $+         snd (last (unwindMacrosAcc coinput+                                    (stringToKeyMacro "'x'yv")))+         @?= "xyy"+     , testCase "RepeatLast test 12" $+         snd (last (unwindMacrosAcc coinput+                                    (listToKeyMacro ["v", "C-v"])))+         @?= ""+     , testCase "RepeatLast test 13" $+         snd (last (unwindMacrosAcc coinput+                                    (listToKeyMacro ["'", "x", "'", "C-v"])))+         @?= "xxxxxxxxxxxxxxxxxxxxxxxxxx"+     , testCase "RepeatLast test 14" $+         snd (last (unwindMacrosAcc coinput+                                    (listToKeyMacro+                                       ["'", "x", "'", "V", "C-v"])))+         @?= "xxxxxxxxxxxxxxxxxxxxxxxxxxx"+     , testCase "RepeatLast test 15" $+         snd (last (unwindMacrosAcc (bindInput [("a", "x")] coinput)+                                    (stringToKeyMacro "av")))+         @?= "xx"+     , testCase "RepeatLast test 16" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'x'")] coinput)+                                    (stringToKeyMacro "'a'v")))+         @?= "xx"+     , testCase "RepeatLast test 17" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'x'v")] coinput)+                                    (stringToKeyMacro "a")))+         @?= "xx"+     , testCase "RepeatLast test 18" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'x'v")] coinput)+                                    (stringToKeyMacro "'a'")))+         @?= "xx"+     , testCase "RepeatLast test 19" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'x'v")] coinput)+                                    (stringToKeyMacro "'a'v")))+         @?= "xxxx"+     , testCase "RepeatLast test 20" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'bz'v")+                                         , ("c", "'aaa'v") ] coinput)+                                    (stringToKeyMacro "c")))+         @?= "bzzbzzbzzbzz"+     , testCase "RepeatLast test 21" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'x'V")] coinput)+                                    (stringToKeyMacro "'a'v")))+         @?= "xxxx"+     , testCase "RepeatLast test 22" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'xy'V")] coinput)+                                    (stringToKeyMacro "'aa'v")))+         @?= "xyxyxyxyxyxy"+     , testCase "RepeatLast test 23" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'xy'v")] coinput)+                                    (stringToKeyMacro "'aa'V")))+         @?= "xyyxyyxyyxyy"+     , testCase "RepeatLast test 24" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'xy'vv")] coinput)+                                    (stringToKeyMacro "'aa'vv")))+         @?= "xyyyxyyyxyyyxyyy"+     , testCase "RepeatLast test 25" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'xyv'v")] coinput)+                                    (stringToKeyMacro "'a'a'vv'")))+         @?= "xyyyxyyyxyyyxyyy"+     , testCase "RepeatLast test 26" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")+                                               , ("b", "'za'v")+                                               , ("c", "'ab'v") ] coinput)+                                    (stringToKeyMacro "'c'v")))+         @?= "xyyzxyyxyyzxyyxyyxyyzxyyxyyzxyyxyy"+     , testCase "RepeatLast test 27" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'V")+                                               , ("b", "'za'v")+                                               , ("c", "'ab'v") ] coinput)+                                    (stringToKeyMacro "'c'v")))+         @?= "xyxyzxyxyxyxyzxyxyxyxyxyxyzxyxyxyxyzxyxyxyxy"+     , testCase "RepeatLast test 28" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")+                                               , ("b", "'za'V")+                                               , ("c", "'ab'v") ] coinput)+                                    (stringToKeyMacro "'c'v")))+         @?= "xyyzxyyzxyyzxyyzxyyxyyzxyyzxyyzxyyzxyy"+     , testCase "RepeatLast test 29" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")+                                               , ("b", "'za'V")+                                               , ("c", "'ab'V") ] coinput)+                                    (stringToKeyMacro "'c'v")))+         @?= "xyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyy"+     , testCase "RepeatLast test 30" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")+                                               , ("b", "'za'V")+                                               , ("c", "'ab'V") ] coinput)+                                    (stringToKeyMacro "'c'V")))+         @?= "xyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyy"+     , testCase "RepeatLast test 31" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")+                                               , ("b", "'za'v")+                                               , ("c", "'ab'V") ] coinput)+                                    (stringToKeyMacro "'c'V")))+         @?= "xyyzxyyxyyxyyzxyyxyyxyyzxyyxyyxyyzxyyxyy"+     , testCase "RepeatLast test 32" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")+                                               , ("b", "'za'v") ] coinput)+                                    (stringToKeyMacro "'ab'vv")))+         @?= "xyyzxyyxyyzxyyxyyzxyyxyy"+     , testCase "RepeatLast test 33" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'V") ] coinput)+                                    (stringToKeyMacro "a'za'vvv")))+         @?= "xyxyzxyxyxyxyxyxyxyxy"+     , testCase "RepeatLast test 34" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")+                                               , ("c", "a'za'Vv") ] coinput)+                                    (stringToKeyMacro "'c'v")))+         @?= "xyyzxyyzxyyzxyyxyyzxyyzxyyzxyy"+     , testCase "RepeatLast test 35" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")+                                               , ("b", "'za'V") ] coinput)+                                    (stringToKeyMacro "'ab'Vv")))+         @?= "xyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyy"+     , testCase "RepeatLast test 36" $+         snd (last (unwindMacrosAcc (bindInput [ ("a", "'xy'v")+                                               , ("b", "za'za'") ] coinput)+                                    (stringToKeyMacro "'ab'V'ab'V")))+         @?= "xyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyyxyyzxyyzxyy"+     , testCase "RepeatLast test 37" $+         snd (last (unwindMacrosAcc (bindInput [ ("b", "z'xy'vv")+                                               , ("c", "'xyvb'V") ] coinput)+                                    (stringToKeyMacro "'c'V")))+         @?= "xyyzxyyyxyyzxyyyxyyzxyyyxyyzxyyy"+     , testCase "RepeatLast test 38" $+         snd (last (unwindMacrosAcc coinput (stringToKeyMacro "'xv'V")))+         @?= "xxxx"+     , testCase "RepeatLast test 39" $+         fst <$> unwindMacros coinput (stringToKeyMacro "'xv'V")+         @?= [[(Right   "", "'xv'V",  "")],+              [(Left    "",  "xv'V",  "")],+              [(Left   "x",   "v'V", "x")],+              [(Left   "x",   "x'V", "x")],+              [(Left  "xx",    "'V", "x")],+              [(Right "xx",     "V", "x")],+              [(Right   "",    "xx",  ""), (Right "xx",    "", "V")],+              [(Right   "",     "x", "x"), (Right "xx",    "", "V")],+              [(Right "xx",      "", "V")]]+     , testCase "RepeatLast test 40" $+         snd (last (unwindMacrosAcc coinput (stringToKeyMacro "'xy'Vv")))+         @?= "xyxyxy"+     , testCase+         "RepeatLast test 41; named macros not referentially transparent" $+         snd (last (unwindMacrosAcc (bindInput [("a", "'xy'V")] coinput)+                                    (stringToKeyMacro "av")))+         @?= "xyxyxyxy"  -- because @a@ repeated; good!+     , testCase "RepeatLast test 42" $+         snd (last (unwindMacrosAcc (bindInput [("a", "xy")] coinput)+                                    (stringToKeyMacro "'a'Vv")))+         @?= "xyxyxy"  -- because @V@ repeated; good!+     , testCase "RepeatLast test 43" $+         snd (last (unwindMacrosAcc coinput (stringToKeyMacro "'xyV'V")))+         @?= "xyxy"+     , testCase "RepeatLast test 44" $+         snd (last (unwindMacrosAcc coinput (stringToKeyMacro "'xyV'v")))+         @?= "xyxy"+     , testCase "RepeatLast test 45" $+         snd (last (unwindMacrosAcc (bindInput [("a", "xyV")] coinput)+                                    (stringToKeyMacro "'a'V")))+         @?= "xyxy"+     , testCase "RepeatLast test 46" $+         snd (last (unwindMacrosAcc (bindInput [("a", "xyV")] coinput)+                                    (stringToKeyMacro "'a'v")))+         @?= "xyxy"+     ]++integrationTests :: TestTree+integrationTests = testGroup "integrationTests" $+  [ testCase "Null frontend; 5 frames" $ do+      let seed = "SMGen 131 141"+          args = words "--dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 5 --automateAll --keepAutomated --gameMode crawl"+                 ++ [ "--setDungeonRng", seed, "--setMainRng", seed]+      serverOptions <- handleParseResult $ execParserPure defaultPrefs serverOptionsPI args+      tieKnot serverOptions+  ]+  +++  let corule = RK.makeData Content.RuleKind.standardRules+      uiOptions = unsafePerformIO $ mkUIOptions corule defClientOptions+      testFontset :: (Int, String) -> TestTree+      testFontset (n, fontsetName) =+        testCase ("SDL fronted; init only; " ++ fontsetName ++ " fontset") $+          when (frontendName == "sdl") $ do+            -- This test only works when run from the same directory that+            -- the .cabal files is in. And this is what Debian needs, so OK.+            -- The hacky log priority 0 tells SDL frontend to init+            -- and quit at once, for testing on CIs without graphics access.+            let seed = "SMGen " ++ show (13 + 2 * n) ++ " " ++ show (15 + 4 * n)+                args2 = words "--dbgMsgSer --logPriority 0 --newGame 3 --maxFps 100000 --benchmark --stopAfterFrames 5 --automateAll --keepAutomated --gameMode battle"+                        ++ [ "--setDungeonRng", seed, "--setMainRng", seed+                           , "--fontset", fontsetName ]+            serverOptions2 <- handleParseResult $ execParserPure defaultPrefs serverOptionsPI args2+            tieKnot serverOptions2+      isUbuntu = ("ubuntu" `T.isPrefixOf`)+  in map testFontset $ zip [0..] $ map T.unpack $ filter (not . isUbuntu)+     $ map fst $ uFontsets uiOptions
− test/test.hs
@@ -1,23 +0,0 @@-{-# LANGUAGE PackageImports #-}-import Prelude ()--import Options.Applicative--import Game.LambdaHack.Client.UI.Frontend.Chosen-import Game.LambdaHack.Core.Prelude-import Game.LambdaHack.Server---- For the case of flattened .cabal file, to avoid ambiguity.-import "Allure" TieKnot--main :: IO ()-main = do-  let args = words "--dbgMsgSer --logPriority 4 --newGame 1 --noAnim --maxFps 100000 --frontendNull --benchmark --stopAfterFrames 50 --automateAll --keepAutomated --gameMode crawl --setDungeonRng 0 --setMainRng 0"-  serverOptions <- handleParseResult $ execParserPure defaultPrefs serverOptionsPI args-  tieKnot serverOptions-  when (frontendName == "sdl") $ do-    -- The hacky log priority 0 tells SDL frontend to init and quit at once,-    -- for testing on CIs without graphics access.-    let args2 = words "--dbgMsgSer --logPriority 0 --newGame 3 --maxFps 100000 --benchmark --stopAfterFrames 50 --automateAll --keepAutomated --gameMode battle --setDungeonRng 7 --setMainRng 7"-    serverOptions2 <- handleParseResult $ execParserPure defaultPrefs serverOptionsPI args2-    tieKnot serverOptions2