packages feed

intricacy 0.9.0.0 → 0.9.1.0

raw patch · 11 files changed

+337/−296 lines, 11 files

Files

Cache.hs view
@@ -81,7 +81,7 @@ makeRequest saddr@(ServerAddr host prt) request =     handle (return . ServerError . (show::SomeException -> String)) $ do         connect host (show prt) makeRequest'-            `catchIO` const (return $ ServerError $ "Cannot connect to "++saddrStr saddr++"!")+            `catchIO` (\e -> return $ ServerError $ "Cannot connect to "++saddrStr saddr++"! "++show e)     where         makeRequest' (sock,_) = do             send sock . BL.toStrict $ encode request
MainState.hs view
@@ -76,6 +76,8 @@         ok <- initUI         if ok then m >>= (endUI >>).return.Just else return Nothing +type TutorialLevel = Maybe Int+ -- | this could be neatened using GADTs data MainState     = PlayState@@ -87,7 +89,7 @@         , psGameStateMoveStack :: [(GameState, PlayerMove)]         , psUndoneStack        :: [PlayerMove]         , psTitle              :: Maybe String-        , psTutLevel           :: Maybe Int+        , psTutLevel           :: TutorialLevel         , psIsSub              :: Bool         , psSaved              :: Bool         , psMarks              :: Map Char MainState@@ -152,6 +154,10 @@     InitState {}   -> IMInit     MetaState {}   -> IMMeta +getTutLevel :: MainState -> TutorialLevel+getTutLevel s@(PlayState {}) = psTutLevel s+getTutLevel _                = Nothing+ newPlayState (frame,st) pms title tutLev sub saved = PlayState st frame [] False False [] pms title tutLev sub saved Map.empty newReplayState st soln title = ReplayState st [] soln [] title Map.empty newEditState (frame,st) msoln mpath = EditState st [] [] frame mpath@@ -182,9 +188,13 @@     } deriving (Eq, Ord, Show, Read) initTutProgress = TutProgress False 1 Nothing -wrenchOnlyTutLevel, noUndoTutLevel :: Maybe Int -> Bool+wrenchOnlyTutLevel, noUndoTutLevel, keysTutLevel, conflictsTutLev :: TutorialLevel -> Bool wrenchOnlyTutLevel = (`elem` (Just <$> [1..4])) noUndoTutLevel = (`elem` (Just <$> [1..7]))+keysTutLevel (Just n) = n >= 5+keysTutLevel _        = False+conflictsTutLev (Just n) = n == 11+conflictsTutLev _        = False  data InitLock = InitLock     { initLockName    :: String
− NEWS
@@ -1,108 +0,0 @@-This is an abbreviated summary; see the git log for gory details.--0.9:-    Switch from SDL1 to SDL2.-    Animate force conflicts.-    Rework default keybindings and click-to-move.-    Allow adjusting sound volume.-    Add tutorial lock and 3 initiation locks.-    Replace inefficient Read-based serialisation of user info with Binary.-0.8.2:-    Show esteem with symbols as well as colour (thanks Locria Cyber)-    Fix relative esteem calculation display-0.8.1:-    Revise tutorial and initiation levels REP and GAP-    Disallow overextended/compressed springs-    Allow editing spring tension by turning-    Add compact solution format-    Revert scoring change introduced in 0.6-    Add --datadir option-    Fix loading partial initiation solutions-    Fix server locking-0.8:-    Add single-player introductory subgame ("initiation").-    Improve UI variously.-0.7.2.3:-    Hopefully work around curses build failures-0.7.2.1:-    Support >=ghc-8.6.5-0.7.2:-    Support ghc-8.0-    Use Argon2 for server-side password hashing-    Tweak graphics and text-0.7.1.1:-    Make server compatible with feed-1.0.0 (thanks constatinus)-    Avoid DOS-reserved codenames (thanks constatinus)-0.7.1:-    Rework tutorial and intro.-    Animate movement.-    Prevent dragging wrench off-path from generating a move.-    Various UI improvements.-0.7.0.1:-    Fix compile error on older ghc versions.-0.7:-    Encrypt passwords when communicating with server-        (in previous versions, they were salted and hashed but not encrypted).-    Clean out solutions when a lock becomes public.-    Various UI improvements, particularly for curses mode.--0.6.2:-    Fix obscure but actual bug in core game physics:-        wrenches were being blocked when they shouldn't be.-        See TST:C on the main server for an example - it can be solved only in-        intricacy versions prior to this one.-    Extra help text screen for editing first lock.-    Cycle through all possibilities when placing blocks in editor.--0.6.1:-    Fix stupid bug preventing registration via keyboard command.--0.6:-    New scoring system - you don't get the point for a solution if the lock-	owner has read your note.-    Server optionally notifies users by email when their locks are solved.-    Server produces RSS feeds.-    Save solutions-in-progress of locks and tutorial.-    Indicate when there's a pending network request, and allow cancellation.-    Support ghc-7.10.-    Support OS X (thanks Kevin Eaves).-    Further tweaks to UI and tutorials.--0.5:-    Adjustments to graphics, tutorial, and metagame UI, to increase clarity.-    Concurrency on server; no more freezes while it checks a solution.-    Misc optimisations.-    Fix sound lag on Windows.-    -0.4:-    Sound effects (thanks linley).-    Animate to show the two physics phases (thanks dormir).-    Help screens.-    Help text for buttons and metagame.-    Deneutralised metagame terminology.-    Tutorial tweaked.-    Various UI improvements (thanks Quicksand-S and dormir)--0.3:-    Mouse control.-    Improvements to curses UI - handle resizing; show keys; allow rebinding.-    Shortened tutorial drastically. Other levels now in 'tutorial-extra/'.-    Improve handling inaccessible servers.-    Added background.-    First version uploaded to hackage.-    Scoring tweak: empty slots are accessed iff all non-empty slots are.-    Detailed game info in README.-    Hover text for UI buttons.-    Marks.-    Lock testing in edit mode.-    Significant bugs fixed:-	editor, lockfile reader, and validity checker were not in accordance-	    on what is allowed as the root of a spring.-	SDL UI was redrawing the screen on every mouse movement.-	curses UI wasn't refreshing properly on async flag.-	stale notes weren't getting refreshed.-	retired display got stuck on changing codename.-	you couldn't move into the bolthole with keyboard commands.-    -0.2:-    First public release
+ NEWS.md view
@@ -0,0 +1,118 @@+This is an abbreviated summary; see the git log for gory details.++# 0.9+* Switch from SDL1 to SDL2.+* Animate force conflicts.+* Rework default keybindings and click-to-move.+* Allow adjusting sound volume.+* Add tutorial lock and 3 initiation locks.+* Replace inefficient Read-based serialisation of user info with Binary.++# 0.8.2+* Show esteem with symbols as well as colour (thanks Locria Cyber)+* Fix relative esteem calculation display++# 0.8.1+* Revise tutorial and initiation levels REP and GAP+* Disallow overextended/compressed springs+* Allow editing spring tension by turning+* Add compact solution format+* Revert scoring change introduced in 0.6+* Add --datadir option+* Fix loading partial initiation solutions+* Fix server locking++# 0.8+* Add single-player introductory subgame ("initiation").+* Improve UI variously.++# 0.7.2.3+* Hopefully work around curses build failures++# 0.7.2.1+* Support >=ghc-8.6.5++# 0.7.2+* Support ghc-8.0+* Use Argon2 for server-side password hashing+* Tweak graphics and text++# 0.7.1.1+* Make server compatible with feed-1.0.0 (thanks constatinus)+* Avoid DOS-reserved codenames (thanks constatinus)++# 0.7.1+* Rework tutorial and intro.+* Animate movement.+* Prevent dragging wrench off-path from generating a move.+* Various UI improvements.++# 0.7.0.1+* Fix compile error on older ghc versions.++# 0.7+* Encrypt passwords when communicating with server+    (in previous versions, they were salted and hashed but not encrypted).+* Clean out solutions when a lock becomes public.+* Various UI improvements, particularly for curses mode.++# 0.6.2+* Fix obscure but actual bug in core game physics:+    wrenches were being blocked when they shouldn't be.+    See TST:C on the main server for an example - it can be solved only in+    intricacy versions prior to this one.+* Extra help text screen for editing first lock.+* Cycle through all possibilities when placing blocks in editor.++# 0.6.1+* Fix stupid bug preventing registration via keyboard command.++# 0.6+* New scoring system - you don't get the point for a solution if the lock+owner has read your note.+* Server optionally notifies users by email when their locks are solved.+* Server produces RSS feeds.+* Save solutions-in-progress of locks and tutorial.+* Indicate when there's a pending network request, and allow cancellation.+* Support ghc-7.10.+* Support OS X (thanks Kevin Eaves).+* Further tweaks to UI and tutorials.++# 0.5+* Adjustments to graphics, tutorial, and metagame UI, to increase clarity.+* Concurrency on server; no more freezes while it checks a solution.+* Misc optimisations.+* Fix sound lag on Windows.++# 0.4+* Sound effects (thanks linley).+* Animate to show the two physics phases (thanks dormir).+* Help screens.+* Help text for buttons and metagame.+* Deneutralised metagame terminology.+* Tutorial tweaked.+* Various UI improvements (thanks Quicksand-S and dormir)++# 0.3+* Mouse control.+* Improvements to curses UI - handle resizing; show keys; allow rebinding.+* Shortened tutorial drastically. Other levels now in 'tutorial-extra/'.+* Improve handling inaccessible servers.+* Added background.+* First version uploaded to hackage.+* Scoring tweak: empty slots are accessed iff all non-empty slots are.+* Detailed game info in README.+* Hover text for UI buttons.+* Marks.+* Lock testing in edit mode.+* Significant bugs fixed:+* editor, lockfile reader, and validity checker were not in accordance+    on what is allowed as the root of a spring.+* SDL UI was redrawing the screen on every mouse movement.+* curses UI wasn't refreshing properly on async flag.+* stale notes weren't getting refreshed.+* retired display got stuck on changing codename.+* you couldn't move into the bolthole with keyboard commands.++# 0.2+* First public release
− README
@@ -1,157 +0,0 @@-# Intricacy-A game of competitive puzzle-design.--https://mbays.sdf.org/intricacy/--## Configuration-You can change keybindings in SDL mode by right-clicking on the corresponding-button and pressing a key.--Config files are saved in `~/.intricacy/` on unixoids, or something like-`...\Application Data\intricacy\` on windows. In particular, the locks you-create in the lock editor are saved in there (and you can organise them into-directories by including (back)slashs in the lock name). You can edit them in-a text editor if you want - see AsciiLock.hs for what the characters mean.--If you want to cheat your way past initiation, change "False" to "True" in-`metagame.conf`.---## Rough description of the game mechanics-* Locks consist of blocks, pivots and balls. Blocks can be connected to other-    blocks by springs. Pivots have arms. When picking a lock, you control two-    tools - a hook, which acts as a mobile one-armed pivot, and a wrench,-    which acts as a mobile block with momentum.-* To open a lock, the "bolthole" (the area in the top-right) must be empty.-* Each turn, the tools push and rotate according to the player's commands, and-    then each spring which is compressed or extended beyond its natural length-    pushes/pulls on the block at its end.-* If two forces try to move a piece in two different directions, or try to-    move two pieces into the same hex, one or both is blocked.-* If a pivot/hook is trying to turn and there's a piece in the way of one of-    its arms, it will at first try to push the piece away - but if that force-    is blocked, it will try instead to pull the piece round with the arm as it-    turns.-* If two springs are trying to push two blocks into the same hex, they will-    generally both be blocked. However, if one of the forces is also pushing-    against a fixed piece such as a tool, it won't block the other force.---## Full details of the metagame mechanics-(Where by the 'game' I mean the lock-picking bit, and by the 'metagame' I mean-the bit with the 3-letter codenames and the three lock slots and notes and so-on. If you haven't seen anything of the kind... keep solving those locks!)--Scoring is always relative - each player has a score relative to each other-player. That score is the number of the second player's lock slots to which-the first player has access, minus the number of the first player's lock slots-to which the second player has access.--A player accesses a lock slot when one of the following holds-    * the player has solved the lock in the slot, and declared the solution;-    * the player has read three notes on the lock in the slot;-    * there's no lock in the slot, and the player has accessed all the slots-	which do have locks in them.--A player reads every note in every lock the player accesses. When a lock is-replaced ('retired'), each note secured by the lock becomes 'public', and is-read by every player.--Note this means that once three notes on a lock become public, every player-accesses the lock. The lock is then 'public' (and its owner should replace-it!).--To declare a solution, you must secure a note on it behind a non-public lock-in one of your three lock slots. Once the note is placed, it can't be moved.---## Full details of the game mechanics-Springs are directed; one of the blocks it is connected to is the 'root', the-other the 'end'. A block is stationary if it is not the end of any spring. The-directed graph whose nodes are the blocks and whose edges are the springs is-required to be acyclic. A spring may also be rooted in a pivot.--I will now attempt to describe in full excruciating detail the game physics,-i.e. the algorithm used to determine what happens on a turn. You shouldn't-need to read this to play the game! Experimenting and turning on blockage-annotations should be enough.--The following description corresponds to the code in Physics.hs. The algorithm-is the result of an extended process of experiment and iterated-simplification; see notes/game in the source distribution if you're perverse-enough to want to read a scattered stream-of-consciousness account of the-process.--Each turn is separated into two phases. In the first phase, the forces arising-from the player's move are 'resolved', possibly resulting in some movement. In-the second phase, forces arising from stretched or compressed springs are-resolved, possibly resulting in some movement.--Here we abuse physics terminology, and use the term 'force' to refer to either-a directional force in the usual sense, which we call a 'push', or a-rotational force, which we call a 'torque'. A force is always on a piece, in-some direction. Only the obvious six hex directions and the two obvious-rotational directions occur; the magnitude is always 1. --Player moves result in forces on the tools in the obvious way. A wrench which-is moving gets a push in that direction each turn (sorry, Newton!). A spring-which is not at its relaxed length results in a push on the end piece.--To _resolve_ these initial forces:-    * each initial force is 'propagated to a force group'-    * if a group is 'inconsistent' with another group: if one of the groups is-        'dominated' by the other, it is 'blocked'; else both are blocked-    * each force in each unblocked force group is 'applied'--To _apply_ a push: move the piece in the given direction.-To _apply_ a torque: rotate the piece in the given direction.--To _propagate_ a force:-    * If applying the force would result in an overlap with another piece,-	push that piece - but if it's an arm, twist the pivot instead when-	that makes sense.-    * If the force is a torque and an arm is pushing on another piece, we have-	a special rule: if the obvious push ends up propagating to a resisted-	force (see below), then it is replaced with a 'clawing' push. e.g. in-	the following situation:-	    \ O #-	     o-	if the pivot is twisted clockwise, the ball will first be pushed east,-	but when that force propagates to a push on the stationary block to-	its east which is resisted, processing will back up and a push on the-	ball southeast will be tried instead.-    * A spring connecting two blocks transmits a push on one block to the-	other unless the direction is such that the push is tending to-        compress resp. extend the spring and the spring is currently extended -        resp. compressed, or the force is a player force and the spring isn't -        already fully compressed resp. extended.--A force is _resisted_ if it is a push on a pivot or on a block which isn't the-    end of a spring, or it's a torque on a block, or it's a force on a hook-    which isn't already getting that force this phase as the result of a-    player move, or it's a force on a wrench which isn't a push in the-    direction it's already moving.--To _propagate an initial force to a force group_:-    * Propagate the force as above, then recursively propagate the resulting-	forces, checking for resistance as we go. The force group consists of-	all the resulting propagated forces.-    * On resistance: back up to try clawing as described above if appropriate,-	else consider the whole force group resisted - so the resulting force-	group is empty.--Two forces are _inconsistent_ when-    * they act on the same piece in different directions, or-    * applying both at once results in overlapping pieces, or-    * applying both at once results in an overextended/overcompressed spring.--Two force groups are _inconsistent_ iff there is some force from the first-    which is inconsistent with some force from the second.--A force group _dominates_ another iff their initial forces are spring forces,-    and the root of the first spring is an ancestor of the root of the second-    spring, where an ancestor of a block B is recursively defined to be a block-    which is the root of a spring which ends at B, or an ancestor of such a-    block.---- mbays@sdf.org 2013
+ README.md view
@@ -0,0 +1,157 @@+# Intricacy+A game of competitive puzzle-design.++https://mbays.sdf.org/intricacy/++## Configuration+You can change keybindings in SDL mode by right-clicking on the corresponding+button and pressing a key.++Config files are saved in `~/.intricacy/` on unixoids, or something like+`...\Application Data\intricacy\` on windows. In particular, the locks you+create in the lock editor are saved in there (and you can organise them into+directories by including (back)slashs in the lock name). You can edit them in+a text editor if you want - see AsciiLock.hs for what the characters mean.++If you want to cheat your way past initiation, change "False" to "True" in+`metagame.conf`.+++## Rough description of the game mechanics+* Locks consist of blocks, pivots and balls. Blocks can be connected to other+    blocks by springs. Pivots have arms. When picking a lock, you control two+    tools - a hook, which acts as a mobile one-armed pivot, and a wrench,+    which acts as a mobile block with momentum.+* To open a lock, the "bolthole" (the area in the top-right) must be empty.+* Each turn, the tools push and rotate according to the player's commands, and+    then each spring which is compressed or extended beyond its natural length+    pushes/pulls on the block at its end.+* If two forces try to move a piece in two different directions, or try to+    move two pieces into the same hex, one or both is blocked.+* If a pivot/hook is trying to turn and there's a piece in the way of one of+    its arms, it will at first try to push the piece away - but if that force+    is blocked, it will try instead to pull the piece round with the arm as it+    turns.+* If two springs are trying to push two blocks into the same hex, they will+    generally both be blocked. However, if one of the forces is also pushing+    against a fixed piece such as a tool, it won't block the other force.+++## Full details of the metagame mechanics+(Where by the 'game' I mean the lock-picking bit, and by the 'metagame' I mean+the bit with the 3-letter codenames and the three lock slots and notes and so+on. If you haven't seen anything of the kind... keep solving those locks!)++Scoring is always relative - each player has a score relative to each other+player. That score is the number of the second player's lock slots to which+the first player has access, minus the number of the first player's lock slots+to which the second player has access.++A player accesses a lock slot when one of the following holds+    * the player has solved the lock in the slot, and declared the solution;+    * the player has read three notes on the lock in the slot;+    * there's no lock in the slot, and the player has accessed all the slots+	which do have locks in them.++A player reads every note in every lock the player accesses. When a lock is+replaced ('retired'), each note secured by the lock becomes 'public', and is+read by every player.++Note this means that once three notes on a lock become public, every player+accesses the lock. The lock is then 'public' (and its owner should replace+it!).++To declare a solution, you must secure a note on it behind a non-public lock+in one of your three lock slots. Once the note is placed, it can't be moved.+++## Full details of the game mechanics+Springs are directed; one of the blocks it is connected to is the 'root', the+other the 'end'. A block is stationary if it is not the end of any spring. The+directed graph whose nodes are the blocks and whose edges are the springs is+required to be acyclic. A spring may also be rooted in a pivot.++I will now attempt to describe in full excruciating detail the game physics,+i.e. the algorithm used to determine what happens on a turn. You shouldn't+need to read this to play the game! Experimenting and turning on blockage+annotations should be enough.++The following description corresponds to the code in Physics.hs. The algorithm+is the result of an extended process of experiment and iterated+simplification; see notes/game in the source distribution if you're perverse+enough to want to read a scattered stream-of-consciousness account of the+process.++Each turn is separated into two phases. In the first phase, the forces arising+from the player's move are 'resolved', possibly resulting in some movement. In+the second phase, forces arising from stretched or compressed springs are+resolved, possibly resulting in some movement.++Here we abuse physics terminology, and use the term 'force' to refer to either+a directional force in the usual sense, which we call a 'push', or a+rotational force, which we call a 'torque'. A force is always on a piece, in+some direction. Only the obvious six hex directions and the two obvious+rotational directions occur; the magnitude is always 1. ++Player moves result in forces on the tools in the obvious way. A wrench which+is moving gets a push in that direction each turn (sorry, Newton!). A spring+which is not at its relaxed length results in a push on the end piece.++To _resolve_ these initial forces:+    * each initial force is 'propagated to a force group'+    * if a group is 'inconsistent' with another group: if one of the groups is+        'dominated' by the other, it is 'blocked'; else both are blocked+    * each force in each unblocked force group is 'applied'++To _apply_ a push: move the piece in the given direction.+To _apply_ a torque: rotate the piece in the given direction.++To _propagate_ a force:+    * If applying the force would result in an overlap with another piece,+	push that piece - but if it's an arm, twist the pivot instead when+	that makes sense.+    * If the force is a torque and an arm is pushing on another piece, we have+	a special rule: if the obvious push ends up propagating to a resisted+	force (see below), then it is replaced with a 'clawing' push. e.g. in+	the following situation:+	    \ O #+	     o+	if the pivot is twisted clockwise, the ball will first be pushed east,+	but when that force propagates to a push on the stationary block to+	its east which is resisted, processing will back up and a push on the+	ball southeast will be tried instead.+    * A spring connecting two blocks transmits a push on one block to the+	other unless the direction is such that the push is tending to+        compress resp. extend the spring and the spring is currently extended +        resp. compressed, or the force is a player force and the spring isn't +        already fully compressed resp. extended.++A force is _resisted_ if it is a push on a pivot or on a block which isn't the+    end of a spring, or it's a torque on a block, or it's a force on a hook+    which isn't already getting that force this phase as the result of a+    player move, or it's a force on a wrench which isn't a push in the+    direction it's already moving.++To _propagate an initial force to a force group_:+    * Propagate the force as above, then recursively propagate the resulting+	forces, checking for resistance as we go. The force group consists of+	all the resulting propagated forces.+    * On resistance: back up to try clawing as described above if appropriate,+	else consider the whole force group resisted - so the resulting force+	group is empty.++Two forces are _inconsistent_ when+    * they act on the same piece in different directions, or+    * applying both at once results in overlapping pieces, or+    * applying both at once results in an overextended/overcompressed spring.++Two force groups are _inconsistent_ iff there is some force from the first+    which is inconsistent with some force from the second.++A force group _dominates_ another iff their initial forces are spring forces,+    and the root of the first spring is an ancestor of the root of the second+    spring, where an ancestor of a block B is recursively defined to be a block+    which is the root of a spring which ends at B, or an ancestor of such a+    block.++-- mbays@sdf.org 2013
SDL2RenderCache.hs view
@@ -298,9 +298,9 @@         sequence_ [ pixelR (FVec (1/3 + i/4) (-1/4)) (bright white) | i <- [-1..1] ]  renderGlyph (VolumeButton vol) = do-    sequence_ [ arcR (FVec (-2/3) 0) r (-20) 20-            (if vol > 0 then bright green else dim red)-        | r <- [1/3] <> [2/3 | vol > 16 || vol == 0] <> [1 | vol > 32 || vol == 0 ] ]+    sequence_ [ arcR (FVec (-2/3) 0) ((fi r+1)/3) (-20) 20+            (if vol > 16*r then bright green else dim red)+        | r <- [0..2] ]     unless (vol > 0) $         aaLineR (innerCorner hw) (innerCorner $ neg hw) $ dim red 
SDL2UI.hs view
@@ -53,6 +53,7 @@ import           InputMode import           KeyBindings import           Lock+import           MainState import           Maxlocksize import           Metagame import           Mundanities@@ -403,9 +404,16 @@     helpOfSelectableFiltered _ s                    = helpOfSelectable s  -data UIOptButton a = UIOptButton { getUIOpt :: UIOptions->a, setUIOpt :: a->UIOptions->UIOptions,-    uiOptVals :: [a], uiOptPos :: HexVec, uiOptGlyph :: a->Glyph, uiOptDescr :: a->String,-    uiOptModes :: [InputMode], onSet :: Maybe (a -> UIM ()) }+data UIOptButton a = UIOptButton+    { getUIOpt      :: UIOptions->a+    , setUIOpt      :: a->UIOptions->UIOptions+    , uiOptVals     :: [a]+    , uiOptPos      :: HexVec+    , uiOptGlyph    :: a->Glyph+    , uiOptDescr    :: a->String+    , uiOptModes    :: [InputMode]+    , uiOptSideText :: a -> TutorialLevel -> Maybe (String, HexVec)+    , onSet         :: Maybe (a -> UIM ()) }  -- non-uniform type, so can't use a list... uiOB1 = UIOptButton useFiveColouring (\v o -> o {useFiveColouring=v}) [True,False]@@ -413,6 +421,7 @@         (\v -> if v then "Adjacent pieces get different colours" else         "Pieces are coloured according to type")         [] -- disabled+        (const $ const Nothing)         Nothing uiOB2 = UIOptButton showBlocks (\v o -> o {showBlocks=v}) [ShowBlocksBlocking,ShowBlocksAll,ShowBlocksNone]         (periphery 0 +^ 2 *^ hu +^ 2 *^ neg hv) ShowBlocksButton@@ -420,7 +429,9 @@             ShowBlocksBlocking -> "Showing conflicting forces"             ShowBlocksAll      -> "Showing conflicting forces and movements they prevent"             ShowBlocksNone     -> "Not showing conflicts")-        [IMPlay, IMReplay] Nothing+        [IMPlay, IMReplay]+        (\a tutLev -> if a == ShowBlocksNone && conflictsTutLev tutLev then Just ("Conflicts -->", 14*^neg hu) else Nothing)+        Nothing uiOB3P = UIOptButton ((!? IMPlay) . whsButtons) (\v o -> o { whsButtons = Map.alter (const v) IMPlay $ whsButtons o })         [Nothing, Just WHSSelected, Just WHSHook, Just WHSWrench]         (periphery 3 +^ 3 *^ hv) WhsButtonsButton@@ -430,23 +441,25 @@                 WHSSelected -> "selected piece; right-click on buttons to rebind"                 WHSWrench   -> "wrench; right-click on buttons to rebind"                 WHSHook     -> "hook; right-click on buttons to rebind")-        [IMPlay] Nothing+        [IMPlay]+        (\a tutLev -> if isNothing a && keysTutLevel tutLev then Just ("<-- KEYS", 3*^hu) else Nothing)+        Nothing uiOB3E = UIOptButton ((!? IMEdit) . whsButtons) (\v o -> o { whsButtons = Map.alter (const v) IMEdit $ whsButtons o })         [Nothing, Just WHSSelected]         (periphery 3 +^ 3 *^ hv) WhsEditButtonsButton         (\case             Nothing -> "Click to show (and rebind) keyboard control buttons."             Just _ -> "Showing buttons for controlling cursor / selected piece; right-click on buttons to rebind")-        [IMEdit] Nothing+        [IMEdit] (const $ const Nothing) Nothing uiOB4 = UIOptButton showButtonText (\v o -> o {showButtonText=v}) [True,False]         (periphery 0 +^ 2 *^ hu +^ 3 *^ hv) ShowButtonTextButton         (\v -> if v then "Help text enabled" else         "Help text disabled")-        [IMPlay, IMEdit, IMReplay, IMMeta, IMInit] Nothing+        [IMPlay, IMEdit, IMReplay, IMMeta, IMInit] (const $ const Nothing) Nothing uiOB5 = UIOptButton fullscreen (\v o -> o {fullscreen=v}) [True,False]         (periphery 0 +^ 4 *^ hu +^ 2 *^ hv) FullscreenButton         (\v -> if v then "Fullscreen mode active" else "Windowed mode active")-        [IMPlay, IMEdit, IMReplay, IMMeta, IMInit] (Just onFullScreenChange)+        [IMPlay, IMEdit, IMReplay, IMMeta, IMInit] (const $ const Nothing) (Just onFullScreenChange) uiOB6 = UIOptButton soundVolume (\v o -> o {soundVolume=v}) [64,0,16,32]         (periphery 0 +^ 4 *^ hu +^ hv) VolumeButton         (\case@@ -454,29 +467,36 @@             v | v >= 32 -> "Sound effects at medium volume"             0           -> "Sound effects disabled"             _           -> "Sound effects at low volume")-        [IMPlay, IMEdit, IMReplay] $ Just (`setVolume` AllChannels)+        [IMPlay, IMEdit, IMReplay] (const $ const Nothing) $ Just (`setVolume` AllChannels) -drawUIOptionButtons :: InputMode -> UIM ()-drawUIOptionButtons mode = do-    drawUIOptionButton mode uiOB1-    drawUIOptionButton mode uiOB2-    drawUIOptionButton mode uiOB3P-    drawUIOptionButton mode uiOB3E-    drawUIOptionButton mode uiOB4-    drawUIOptionButton mode uiOB5+drawUIOptionButtons :: InputMode -> TutorialLevel -> UIM ()+drawUIOptionButtons mode tutLev = do+    drawUIOptionButton mode tutLev uiOB1+    drawUIOptionButton mode tutLev uiOB2+    drawUIOptionButton mode tutLev uiOB3P+    drawUIOptionButton mode tutLev uiOB3E+    drawUIOptionButton mode tutLev uiOB4+    drawUIOptionButton mode tutLev uiOB5 #ifdef SOUND-    drawUIOptionButton mode uiOB6+    drawUIOptionButton mode tutLev uiOB6 #endif-drawUIOptionButton im b = when (im `elem` uiOptModes b) $ do+drawUIOptionButton im tutLev b = when (im `elem` uiOptModes b) $ do     value <- gets $ getUIOpt b . uiOptions     renderToMain $ mapM_ (\g -> drawAtRel g (uiOptPos b))         [HollowGlyph $ obscure purple, uiOptGlyph b value]+    maybe (pure ()) drawSideText $ uiOptSideText b value tutLev+    where+    drawSideText (s,dv) = do+        smallFont <- gets dispFontSmall+        renderToMain $ withFont smallFont $ recentreAt (uiOptPos b) $ rescaleRender (1/4) $+            renderStrColAtLeft white s dv+ describeUIOptionButton :: UIOptButton a -> MaybeT UIM String describeUIOptionButton b = do     value <- gets $ getUIOpt b . uiOptions     return $ uiOptDescr b value -- XXX: hand-hacking lenses...-toggleUIOption (UIOptButton getopt setopt vals _ _ _ _ monSet) = do+toggleUIOption (UIOptButton getopt setopt vals _ _ _ _ _ monSet) = do     value <- gets $ getopt . uiOptions     let value' = cycle vals !! max 0 (1 + fromMaybe 0 (elemIndex value vals))     modifyUIOptions $ setopt value'
SDL2UIMInstance.hs view
@@ -61,12 +61,13 @@         lift $ clearButtons >> clearSelectables         s <- get         let mode = ms2im s+            tutLev = getTutLevel s         lift waitFrame         drawMainState' s         lift . drawTitle =<< getTitle         lift $ do             drawButtons mode-            drawUIOptionButtons mode+            drawUIOptionButtons mode tutLev             updateHoverStr mode             drawMsgLine             drawShortMouseHelp mode s
intricacy.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.2 name:               intricacy-version:            0.9.0.0+version:            0.9.1.0 license:            GPL-3.0-or-later license-file:       COPYING maintainer:         mbays@sdf.org@@ -33,9 +33,9 @@ extra-source-files:     Main_stub.h extra-doc-files:-    README+    README.md     BUILD-    NEWS+    NEWS.md     tutorial-extra/*.lock     tutorial-extra/README 
tutorial/05-hook.text view
@@ -1,1 +1,1 @@-turn hook with mouse wheel (or keys) to pull springs aside+turn hook with mouse wheel or keys to pull springs aside