1 -- This file is part of Intricacy
2 -- Copyright (C) 2013 Martin Bays <mbays@sdf.org>
4 -- This program is free software: you can redistribute it and/or modify
5 -- it under the terms of version 3 of the GNU General Public License as
6 -- published by the Free Software Foundation, or any later version.
8 -- You should have received a copy of the GNU General Public License
9 -- along with this program. If not, see http://www.gnu.org/licenses/.
12 {-# LANGUAGE FlexibleContexts #-}
13 {-# LANGUAGE FlexibleInstances #-}
14 module SDLUIMInstance
() where
16 import Control
.Applicative
17 import Control
.Concurrent
(threadDelay
)
18 import Control
.Concurrent
.STM
19 import Control
.Monad
.State
20 import Control
.Monad
.Trans
.Maybe
21 import Control
.Monad
.Trans
.Reader
23 import Data
.Foldable
(for_
)
24 import Data
.Function
(on
)
27 import qualified Data
.Map
as Map
29 import qualified Data
.Vector
as Vector
31 import Graphics
.UI
.SDL
hiding (flip, name
)
32 import qualified Graphics
.UI
.SDL
as SDL
33 import qualified Graphics
.UI
.SDL
.TTF
as TTF
34 import Safe
(maximumBound
)
36 --import Debug.Trace (traceShow)
56 instance UIMonad
(StateT UIState
IO) where
57 runUI m
= evalStateT m nullUIState
59 lift
$ clearButtons
>> clearSelectables
64 lift
. drawTitle
=<< getTitle
67 drawUIOptionButtons mode
70 drawShortMouseHelp mode s
72 clearMessage
= clearMsg
74 drawPrompt full s
= say
$ s
++ (if full
then "" else "_")
78 reportAlerts
= playAlertSounds
80 getChRaw
= resetMouseButtons
>> getChRaw
'
82 resetMouseButtons
= modify
$ \s
-> s
83 { leftButtonDown
= Nothing
84 , middleButtonDown
= Nothing
85 , rightButtonDown
= Nothing
88 events
<- liftIO getEvents
89 if not.null $ [ True | MouseButtonDown _ _ ButtonRight
<- events
]
91 else maybe getChRaw
' (return.Just
) $ listToMaybe $ [ ch
92 | KeyDown
(Keysym _ _ ch
) <- events
95 setUIBinding mode cmd ch
=
96 modify
$ \s
-> s
{ uiKeyBindings
=
97 Map
.insertWith
(\ [bdg
] bdgs
-> if bdg `
elem` bdgs
then delete bdg bdgs
else bdg
:bdgs
)
98 mode
[(ch
,cmd
)] $ uiKeyBindings s
}
100 getUIBinding mode cmd
= ($cmd
) <$> getBindingStr mode
102 initUI
= (isJust <$>) . runMaybeT
$ do
103 catchIOErrorMT
$ SDL
.init
105 [InitVideo
,InitAudio
]
109 catchIOErrorMT TTF
.init
116 liftIO
$ warpMouse
(fi
$ w`
div`
2) (fi
$ h`
div`
2)
121 catchIOErrorMT m
= MaybeT
. liftIO
. ignoreIOErrAlt
$ m
>> return (Just
())
128 unblockInput
= return $ pushEvent VideoExpose
132 impatience ticks
= do
133 liftIO
$ threadDelay
50000
135 let pos
= serverWaitPos
136 smallFont
<- gets dispFontSmall
138 mapM_ (drawAtRel
(FilledHexGlyph
$ bright black
)) [ pos
+^ i
*^hu | i
<- [0..3] ]
140 renderStrColAtLeft errorCol
("waiting..."++replicate ((ticks`
div`
5)`
mod`
3) '.') pos
142 registerButton
(pos
+^ neg hv
) CmdQuit
0 [("abort",hu
+^neg hw
)]
143 drawButtons IMImpatience
145 cmds
<- getInput IMImpatience
146 return $ CmdQuit `
elem` cmds
150 (scrCentre
, size
) <- getGeom
151 centre
<- gets dispCentre
152 let SVec x y
= hexVec2SVec size
(pos
-^centre
) +^ scrCentre
153 liftIO
$ warpMouse
(fi x
) (fi y
)
154 lbp
<- gets leftButtonDown
155 rbp
<- gets rightButtonDown
156 let [lbp
',rbp
'] = ((const $ pos
-^ centre
) <$>) <$> [lbp
,rbp
]
157 modify
$ \s
-> s
{leftButtonDown
= lbp
', rightButtonDown
= rbp
'}
160 centre
<- gets dispCentre
161 gets
((Just
.(+^centre
).fst) . mousePos
)
165 registerButton
(periphery
5 +^ hw
+^ neg hv
) (CmdInputChar
'Y
') 2 [("confirm",hu
+^neg hw
)]
166 drawButtons IMTextInput
169 toggleColourMode
= modify
$ \s
-> s
{uiOptions
= (uiOptions s
){
170 useFiveColouring
= not $ useFiveColouring
$ uiOptions s
}}
174 events
<- liftIO
$ nubMouseMotions
<$> getEventsTimeout
(10^
6`
div`fps
)
175 (cmds
,uiChanged
) <- if null events
then return ([],False) else do
177 cmds
<- concat <$> mapM processEvent events
178 setPaintFromCmds cmds
180 return (cmds
,uistatesMayVisiblyDiffer oldUIState newUIState
)
181 now
<- liftIO getTicks
182 animFrameReady
<- gets
(maybe False (<now
) . nextAnimFrameAt
)
183 unless (null cmds
) clearMsg
184 return $ cmds
++ [CmdRefresh | uiChanged || animFrameReady
]
186 nubMouseMotions evs
=
187 -- drop all but last mouse motion event
188 let nubMouseMotions
' False (mm
@MouseMotion
{}:evs
) = mm
:nubMouseMotions
' True evs
189 nubMouseMotions
' True (mm
@MouseMotion
{}:evs
) = nubMouseMotions
' True evs
190 nubMouseMotions
' b
(ev
:evs
) = ev
:nubMouseMotions
' b evs
191 nubMouseMotions
' _
[] = []
192 in reverse $ nubMouseMotions
' False $ reverse evs
193 setPaintFromCmds cmds
= sequence_
194 [ modify
$ \s
-> s
{ paintTileIndex
= pti
}
195 |
(pti
,pt
) <- zip [0..] paintTiles
197 , (isNothing pt
&& cmd
== CmdDelete
) ||
200 CmdTile t
<- Just cmd
201 guard $ ((==)`on`tileType
) t pt
') ]
203 uistatesMayVisiblyDiffer uis1 uis2
=
204 uis1
{ mousePos
= (zero
,False), lastFrameTicks
=0 }
205 /= uis2
{mousePos
= (zero
,False), lastFrameTicks
=0 }
206 processEvent
(KeyDown
(Keysym _ _ ch
)) = case mode
of
207 IMTextInput
-> return [CmdInputChar ch
]
209 setting
<- gets settingBinding
210 if isJust setting
&& ch
/= '\0'
212 modify
$ \s
-> s
{settingBinding
= Nothing
}
213 when (ch
/= '\ESC
') $ setUIBinding mode
(fromJust setting
) ch
216 uibdgs
<- gets
(Map
.findWithDefault
[] mode
. uiKeyBindings
)
217 let mCmd
= lookup ch
$ uibdgs
++ bindings mode
218 return $ maybeToList mCmd
219 processEvent MouseMotion
{} = do
220 (oldMPos
,_
) <- gets mousePos
221 (pos
@(mPos
,_
),(sx
,sy
,sz
)) <- getMousePos
222 updateMousePos mode pos
223 lbp
<- gets leftButtonDown
224 rbp
<- gets rightButtonDown
225 centre
<- gets dispCentre
226 let drag
:: Maybe HexVec
-> Maybe Command
228 fromPos
@(HexVec x y z
) <- bp
229 -- check we've dragged at least a full hex's distance:
230 guard $ not.all (\(a
,b
) -> abs (fi a
- b
) < 1.0) $ [(x
,sx
),(y
,sy
),(z
,sz
)]
231 let dir
= hexVec2HexDirOrZero
$ mPos
-^ fromPos
233 return $ CmdDrag
(fromPos
+^centre
) dir
235 IMEdit
-> case drag rbp
of
236 Just cmd
-> return [cmd
]
237 Nothing
-> if mPos
/= oldMPos
239 pti
<- getEffPaintTileIndex
240 return $ CmdMoveTo
(mPos
+^ centre
) :
241 ([CmdPaintFromTo
(paintTiles
!!pti
) (oldMPos
+^centre
) (mPos
+^centre
) |
isJust lbp
])
243 IMPlay
-> return $ maybeToList $ msum $ map drag
[lbp
, rbp
]
246 mouseFromTo from to
= do
247 let dir
= hexVec2HexDirOrZero
$ to
-^ from
249 then (CmdDir WHSSelected dir
:) <$> mouseFromTo
(from
+^ dir
) to
251 processEvent
(MouseButtonDown _ _ ButtonLeft
) = do
252 pos
@(mPos
,central
) <- gets mousePos
253 modify
$ \s
-> s
{ leftButtonDown
= Just mPos
}
254 rb
<- gets
(isJust . rightButtonDown
)
255 mcmd
<- cmdAtMousePos pos mode
(Just
False)
256 let hotspotAction
= listToMaybe
257 $ map (\cmd
-> return [cmd
]) (maybeToList mcmd
)
258 ++ [ modify
(\s
-> s
{paintTileIndex
= i
}) >> return []
259 | i
<- take (length paintTiles
) [0..]
260 , mPos
== paintButtonStart
+^ i
*^hv
]
261 ++ [ toggleUIOption uiOB1
>> updateHoverStr mode
>> return []
262 | mPos
== uiOptPos uiOB1
&& mode `
elem` uiOptModes uiOB1
]
263 ++ [ toggleUIOption uiOB2
>> updateHoverStr mode
>> return []
264 | mPos
== uiOptPos uiOB2
&& mode `
elem` uiOptModes uiOB2
]
265 ++ [ toggleUIOption uiOB3
>> updateHoverStr mode
>> return []
266 | mPos
== uiOptPos uiOB3
&& mode `
elem` uiOptModes uiOB3
]
267 ++ [ toggleUIOption uiOB4
>> updateHoverStr mode
>> return []
268 | mPos
== uiOptPos uiOB4
&& mode `
elem` uiOptModes uiOB4
]
269 ++ [ toggleUIOption uiOB5
>> updateHoverStr mode
>> return []
270 | mPos
== uiOptPos uiOB5
&& mode `
elem` uiOptModes uiOB5
]
272 ++ [ toggleUIOption uiOB6
>> updateHoverStr mode
>> return []
273 | mPos
== uiOptPos uiOB6
&& mode `
elem` uiOptModes uiOB6
]
277 then return [ CmdWait
]
278 else flip fromMaybe hotspotAction
$ case mode
of
280 pti
<- getEffPaintTileIndex
281 return [ drawCmd
(paintTiles
!!pti
) False ]
283 centre
<- gets dispCentre
284 return [ CmdManipulateToolAt
$ mPos
+^ centre
]
286 processEvent
(MouseButtonUp _ _ ButtonLeft
) = do
287 modify
$ \s
-> s
{ leftButtonDown
= Nothing
}
289 processEvent
(MouseButtonDown _ _ ButtonRight
) = do
290 pos
@(mPos
,_
) <- gets mousePos
291 modify
$ \s
-> s
{ rightButtonDown
= Just mPos
}
292 lb
<- gets
(isJust . leftButtonDown
)
294 then return [ CmdWait
]
295 else (fromMaybe [] <$>) $ runMaybeT
$ msum
297 cmd
<- MaybeT
$ cmdAtMousePos pos mode Nothing
298 guard $ mode
/= IMTextInput
299 -- modify $ \s -> s { settingBinding = Just cmd }
300 return [ CmdBind
$ Just cmd
]
302 cmd
<- MaybeT
$ cmdAtMousePos pos mode
(Just
True)
305 IMPlay
-> return [ CmdClear
, CmdWait
]
306 _
-> return [ CmdClear
, CmdSelect
] ]
307 processEvent
(MouseButtonUp _ _ ButtonRight
) = do
308 modify
$ \s
-> s
{ rightButtonDown
= Nothing
}
309 return [ CmdUnselect | mode
== IMEdit
]
310 processEvent
(MouseButtonDown _ _ ButtonWheelUp
) = doWheel
1
311 processEvent
(MouseButtonDown _ _ ButtonWheelDown
) = doWheel
$ -1
312 processEvent
(MouseButtonDown _ _ ButtonMiddle
) = do
313 (mPos
,_
) <- gets mousePos
314 modify
$ \s
-> s
{ middleButtonDown
= Just mPos
}
315 rb
<- gets
(isJust . rightButtonDown
)
316 return $ [CmdDelete | rb
]
317 processEvent
(MouseButtonUp _ _ ButtonMiddle
) = do
318 modify
$ \s
-> s
{ middleButtonDown
= Nothing
}
320 processEvent
(VideoResize w h
) = do
323 processEvent VideoExpose
= return [ CmdRefresh
]
324 processEvent Quit
= return [ CmdForceQuit
]
326 processEvent _
= return []
329 rb
<- gets
(isJust . rightButtonDown
)
330 mb
<- gets
(isJust . middleButtonDown
)
331 if ((rb || mb || mode
== IMReplay
) && mode
/= IMEdit
)
332 ||
(mb
&& mode
== IMEdit
)
333 then return [ if dw
== 1 then CmdRedo
else CmdUndo
]
334 else if mode
/= IMEdit || rb
335 then return [ CmdRotate WHSSelected dw
]
337 modify
$ \s
-> s
{ paintTileIndex
= (paintTileIndex s
+ dw
) `
mod`
length paintTiles
}
341 drawCmd mt
True = CmdPaint mt
342 drawCmd
(Just t
) False = CmdTile t
343 drawCmd Nothing _
= CmdDelete
345 getMousePos
:: UIM
((HexVec
,Bool),(Double,Double,Double))
347 (scrCentre
, size
) <- getGeom
348 (x
,y
,_
) <- lift getMouseState
349 let sv
= SVec
(fi x
) (fi y
) +^ neg scrCentre
350 let mPos
@(HexVec x y z
) = sVec2HexVec size sv
351 let (sx
,sy
,sz
) = sVec2dHV size sv
352 let isCentral
= all (\(a
,b
) -> abs (fi a
- b
) < 0.5)
353 [(x
,sx
),(y
,sy
),(z
,sz
)]
354 return ((mPos
,isCentral
),(sx
,sy
,sz
))
355 updateMousePos mode newPos
= do
356 oldPos
<- gets mousePos
357 when (newPos
/= oldPos
) $ do
358 modify
$ \ds
-> ds
{ mousePos
= newPos
}
361 showHelp mode HelpPageInput
= do
362 bdgs
<- nub <$> getBindings mode
363 smallFont
<- gets dispFontSmall
366 let extraHelpStrs
= (["Mouse commands:", "Hover over a button to see keys, right-click to rebind;"]
368 IMPlay
-> ["Click on tool to select, drag to move;",
369 "Click by tool to move; right-click to wait;", "Scroll wheel to rotate hook;",
370 "Scroll wheel with right button held down to undo/redo."]
371 IMEdit
-> ["Left-click to draw selected; scroll to change selection;",
372 "Right-click on piece to select, drag to move;",
373 "While holding right-click: left-click to advance time, middle-click to delete;",
374 "Scroll wheel to rotate selected piece; scroll wheel while held down to undo/redo."]
375 IMReplay
-> ["Scroll wheel for undo/redo."]
376 IMMeta
-> ["Left-clicking on something does most obvious thing;"
377 , "Right-clicking does second-most obvious thing."])
380 "Basic game instructions:"
381 , "Choose [C]odename, then [R]egister it;"
382 , "select other players, and [S]olve their locks;"
383 , "go [H]ome, then [E]dit and [P]lace a lock of your own;"
384 , "you can then [D]eclare your solutions."
385 , "Make other players green by solving their locks and not letting them solve yours."]]
388 renderStrColAtCentre cyan
"Keybindings:" $ (screenHeightHexes`
div`
4)*^
(hv
+^neg hw
)
389 let keybindingsHeight
= screenHeightHexes
- (3 + length extraHelpStrs
+ sum (map length extraHelpStrs
))
390 bdgWidth
= (screenWidthHexes
-6) `
div`
3
391 showKeys chs
= intercalate
"/" (map showKeyFriendly chs
)
392 sequence_ [ with
$ renderStrColAtLeft messageCol
393 ( keysStr
++ ": " ++ desc
)
394 $ (x
*bdgWidth
-(screenWidthHexes
-6)`
div`
2)*^hu
+^ neg hv
+^
395 (screenHeightHexes`
div`
4 - y`
div`
2)*^
(hv
+^neg hw
) +^
397 |
((keysStr
,with
,desc
),(x
,y
)) <- zip [(keysStr
,with
,desc
)
398 |
group <- groupBy ((==) `on`
snd) $ sortBy (compare `on`
snd) bdgs
399 , let cmd
= snd $ head group
400 , let desc
= describeCommand cmd
402 , let chs
= map fst group
403 , let keysStr
= showKeys chs
404 , let with
= if True -- 3*(bdgWidth-1) < length desc + length keysStr + 1
405 then withFont smallFont
408 (map (`
divMod` keybindingsHeight
) [0..])
409 , (x
+1)*bdgWidth
< screenWidthHexes
]
410 sequence_ [ renderStrColAtCentre
(if firstLine
then cyan
else messageCol
) str
411 $ (screenHeightHexes`
div`
4 - y`
div`
2)*^
(hv
+^neg hw
)
414 |
((str
,firstLine
),y
) <- intercalate
[("",False)] (map (`
zip`
415 (True:repeat False)) extraHelpStrs
) `
zip`
416 --[(keybindingsHeight+1)..]
417 [((screenHeightHexes
- sum (length <$> extraHelpStrs
)) `
div`
2)..]
421 showHelp IMInit HelpPageGame
= do
422 renderToMain
$ drawBasicHelpPage
("INTRICACY",red
) (initiationHelpText
,purple
)
424 showHelp IMMeta HelpPageGame
= do
425 renderToMain
$ drawBasicHelpPage
("INTRICACY",red
) (metagameHelpText
,purple
)
427 showHelp IMMeta
(HelpPageInitiated n
) = do
428 renderToMain
$ drawBasicHelpPage
("Initiation complete",purple
) (initiationCompleteText n
,red
)
430 showHelp IMEdit HelpPageFirstEdit
= do
431 renderToMain
$ drawBasicHelpPage
("Your first lock:",purple
) (firstEditHelpText
,green
)
433 showHelp _ _
= return False
435 onNewMode mode
= clearMsg
439 modify
$ \uiState
-> uiState
{bgSurface
=Nothing
}
441 isNothing <$> gets bgSurface
>>?
442 modify
(\uiState
-> uiState
{bgSurface
=bg
})
444 drawMainState
' :: MainState
-> MainStateT UIM
()
445 drawMainState
' PlayState
{ psCurrentState
=st
, psLastAlerts
=alerts
,
446 wrenchSelected
=wsel
, psTutLevel
=tutLevel
, psSolved
=solved
} = do
447 canUndo
<- gets
(null . psGameStateMoveStack
)
448 canRedo
<- gets
(null . psUndoneStack
)
449 let isTut
= isJust tutLevel
451 let selTools
= [ idx |
452 (idx
, PlacedPiece pos p
) <- enumVec
$ placedPieces st
453 , (wsel
&& isWrench p
) ||
(not wsel
&& isHook p
) ]
454 drawMainGameState selTools
False alerts st
455 lb
<- gets
(isJust . leftButtonDown
)
456 rb
<- gets
(isJust . leftButtonDown
)
458 centre
<- gets dispCentre
460 [ registerSelectable
(pos
-^ centre
) 0 $
461 if isWrench p
then SelToolWrench
else SelToolHook
463 , PlacedPiece pos p
<- Vector
.toList
$ placedPieces st
465 unless (noUndoTutLevel tutLevel
) $ do
466 registerUndoButtons canUndo canRedo
467 registerButtonGroup markButtonGroup
468 registerButton
(periphery
0) CmdOpen
(if solved
then 2 else 0) $
469 ("open", hu
+^neg hw
) : [("Press-->",9*^neg hu
) | solved
&& isTut
]
470 drawMainState
' ReplayState
{ rsCurrentState
=st
, rsLastAlerts
=alerts
} = do
471 canUndo
<- gets
(null . rsGameStateMoveStack
)
472 canRedo
<- gets
(null . rsMoveStack
)
474 drawMainGameState
[] False alerts st
475 registerUndoButtons canUndo canRedo
476 renderToMain
$ drawCursorAt Nothing
477 drawMainState
' EditState
{ esGameState
=st
, esGameStateStack
=sts
, esUndoneStack
=undostack
,
478 selectedPiece
=selPiece
, selectedPos
=selPos
} = lift
$ do
479 drawMainGameState
(maybeToList selPiece
) True [] st
480 renderToMain
$ drawCursorAt
$ if isNothing selPiece
then Just selPos
else Nothing
481 registerUndoButtons
(null sts
) (null undostack
)
482 when (isJust selPiece
) $ mapM_ registerButtonGroup
483 [ singleButton
(periphery
2 +^
3*^hw
+^hv
) CmdDelete
0 [("delete",hu
+^neg hw
)]
484 , singleButton
(periphery
2 +^
3*^hw
) CmdMerge
1 [("merge",hu
+^neg hw
)]
487 [ unless (any (pred . placedPiece
) . Vector
.toList
$ placedPieces st
)
488 $ registerButton
(periphery
0 +^ d
) cmd
2 [("place",hu
+^neg hw
),(tool
,hu
+^neg hv
)]
489 |
(pred,tool
,cmd
,d
) <- [
490 (isWrench
, "wrench", CmdTile
$ WrenchTile zero
, (-4)*^hv
+^ hw
),
491 (isHook
, "hook", CmdTile HookTile
, (-3)*^hv
+^ hw
) ] ]
493 drawMainState
' InitState
{initLocks
=initLocks
, tutProgress
=TutProgress
{tutSolved
=tutSolved
}} = lift
$ do
494 renderToMain
(erase
>> drawCursorAt Nothing
)
495 renderToMain
. renderStrColAtCentre white
"I N T R I C A C Y" $ 3 *^
(hv
+^ neg hw
)
497 mapM_ drawInitLock
$ Map
.keys accessible
498 registerButton
(tutPos
+^
3 *^ neg hu
+^ hv
) (CmdSolveInit Nothing
) 2
499 [("solve",hu
+^neg hw
),("lock",hu
+^neg hv
)]
501 accessible
= accessibleInitLocks tutSolved initLocks
502 tutPos
= (maximumBound
0 $ hx
<$> Map
.keys accessible
) *^ neg hu
503 name v | v
== zero
= "TUT"
504 |
otherwise = maybe "???" initLockName
$ Map
.lookup v accessible
505 solved v | v
== zero
= tutSolved
506 |
otherwise = Just
True == (initLockSolved
<$> Map
.lookup v accessible
)
508 let pos
= tutPos
+^
2 *^ v
509 drawNameCol
(name v
) pos
$ if solved v
then brightish green
else brightish yellow
510 renderToMain
$ sequence_
511 [ (if open
then PathGlyph h
$ brightish white
512 else GateGlyph h
$ (if inbounds
then dim
else bright
) white
)
513 `drawAtRel`
(pos
+^ h
)
516 , let inbounds
= abs (hy v
') < 2 && hx v
' >= 0 && hz v
' <= 0
517 , (v
' `
notElem` Map
.keys accessible
) || h `
elem`
[hu
, neg hw
, neg hv
]
518 , let open
= inbounds
&& (solved v || solved v
') ]
519 registerSelectable pos
0 $ if v
== zero
then SelTut
(solved v
) else SelInitLock v
(solved v
)
520 drawMainState
' MetaState
{curServer
=saddr
, undeclareds
=undecls
,
521 cacheOnly
=cOnly
, curAuth
=auth
, codenameStack
=names
,
522 randomCodenames
=rnamestvar
, retiredLocks
=mretired
, curLockPath
=path
,
523 curLock
=mlock
, listOffset
=offset
, asyncCount
=count
} = do
524 let ourName
= authUser
<$> auth
525 let selName
= listToMaybe names
526 let home
= isJust ourName
&& ourName
== selName
527 lift
$ renderToMain
(erase
>> drawCursorAt Nothing
)
529 smallFont
<- gets dispFontSmall
530 renderToMain
$ withFont smallFont
$ renderStrColAtLeft purple
531 (saddrStr saddr
++ if cOnly
then " (offline mode)" else "")
534 when (length names
> 1) $ lift
$ registerButton
535 (codenamePos
+^ neg hu
+^
2*^hw
) CmdBackCodename
0 [("back",3*^hw
)]
538 name
<- MaybeT
(return selName
)
539 FetchedRecord fresh err muirc
<- lift
$ getUInfoFetched
300 name
540 pending
<- ((>0) <$>) $ liftIO
$ readTVarIO count
543 unless ((fresh
&& not pending
) || cOnly
) $ do
544 smallFont
<- gets dispFontSmall
545 let str
= if pending
then "(response pending)" else "(updating)"
546 renderToMain
$ withFont smallFont
$
547 renderStrColBelow
(opaquify
$ dim errorCol
) str codenamePos
548 maybe (return ()) (setMsgLineNoRefresh errorCol
) err
549 when (fresh
&& (isNothing ourName ||
isNothing muirc || home
)) $
550 let reg
= isNothing muirc ||
isJust ourName
551 in registerButton
(codenamePos
+^
2*^hu
)
552 (if reg
then CmdRegister
$ isJust ourName
else CmdAuth
)
553 (if isNothing ourName
then 2 else 0)
554 [(if reg
then "reg" else "auth", 3*^hw
)]
555 (if isJust muirc
then drawName
else drawNullName
) name codenamePos
556 lift
$ registerSelectable codenamePos
0 (SelSelectedCodeName name
)
557 drawRelScore name
(codenamePos
+^hu
)
558 when (isJust muirc
) $ lift
$
559 registerButton retiredPos CmdShowRetired
5 [("retired",hu
+^neg hw
)]
560 for_ muirc
$ \(RCUserInfo
(_
,uinfo
)) -> case mretired
of
563 (map (locksPos
+^
) $ zero
:[rotate n
$ 4*^hu
-^
4*^hw | n
<- [0,2,3,5]])
564 [ \pos
-> lift
(registerSelectable pos
1 (SelOldLock ls
)) >> drawOldLock ls pos
566 lift
$ registerButton
(retiredPos
+^ hv
) (CmdPlayLockSpec Nothing
) 1 [("play",hu
+^neg hw
),("no.",hu
+^neg hv
)]
568 sequence_ [ drawLockInfo
(ActiveLock
(codename uinfo
) i
) mlockinfo |
569 (i
,mlockinfo
) <- assocs $ userLocks uinfo
]
570 when (isJust $ msum $ elems $ userLocks uinfo
) $ lift
$ do
571 registerButton interactButtonsPos
(CmdSolve Nothing
) 2 [("solve",hu
+^neg hw
),("lock",hu
+^neg hv
)]
572 when (isJust ourName
) $
573 registerButton
(interactButtonsPos
+^hw
) (CmdViewSolution Nothing
) 1 [("view",hu
+^neg hw
),("soln",hu
+^neg hv
)]
576 lift
.renderToMain
$ renderStrColAt messageCol
577 "Home" (codenamePos
+^hw
+^neg hv
)
578 unless (null undecls
) $ do
579 lift
.renderToMain
$ renderStrColAtLeft messageCol
"Undeclared:" (undeclsPos
+^
2*^hv
+^neg hu
)
580 lift
$ registerButton
(undeclsPos
+^hw
+^neg hu
) (CmdDeclare Nothing
) 2 [("decl",hv
+^
4*^neg hu
),("soln",hw
+^
4*^neg hu
)]
581 fillArea
(undeclsPos
+^hv
)
582 (map (undeclsPos
+^
) $ hexDisc
1 ++ [hu
+^neg hw
, neg hu
+^hv
])
583 [ \pos
-> lift
(registerSelectable pos
0 (SelUndeclared undecl
)) >> drawActiveLock al pos
584 | undecl
@(Undeclared _ _ al
) <- undecls
]
586 maybe (drawEmptyMiniLock miniLockPos
)
587 (`drawMiniLock` miniLockPos
)
589 registerSelectable miniLockPos
1 SelOurLock
590 registerButton
(miniLockPos
+^
3*^neg hw
+^
2*^hu
) CmdEdit
2
591 [("edit",hu
+^neg hw
),("lock",hu
+^neg hv
)]
592 registerButton lockLinePos CmdSelectLock
1 []
593 lift
$ unless (null path
) $ do
594 renderToMain
$ renderStrColAtLeft messageCol
(take 16 path
) $ lockLinePos
+^ hu
595 registerSelectable
(lockLinePos
+^
2*^hu
) 1 SelLockPath
597 [ registerButton
(miniLockPos
+^
2*^neg hv
+^
2*^hu
+^ dv
) cmd
1
598 [(dirText
,hu
+^neg hw
),("lock",hu
+^neg hv
)]
599 |
(dv
,cmd
,dirText
) <- [(zero
,CmdPrevLock
,"prev"),(neg hw
,CmdNextLock
,"next")] ]
600 let tested
= maybe False (isJust.snd) mlock
601 when (isJust mlock
&& home
) $ lift
$ registerButton
602 (miniLockPos
+^
2*^neg hw
+^
3*^hu
) (CmdPlaceLock Nothing
)
603 (if tested
then 2 else 1)
604 [("place",hu
+^neg hw
),("lock",hu
+^neg hv
)]
605 rnames
<- liftIO
$ readTVarIO rnamestvar
606 unless (null rnames
) $
607 fillArea randomNamesPos
608 (map (randomNamesPos
+^
) $ hexDisc
2)
609 [ \pos
-> lift
(registerSelectable pos
0 (SelRandom name
)) >> drawName name pos
612 when (ourName
/= selName
) $ void
$ runMaybeT
$ do
613 when (isJust ourName
) $
614 lift
.lift
$ registerButton
(codenamePos
+^ hw
+^ neg hv
) CmdHome
1 [("home",3*^hw
)]
615 sel
<- liftMaybe selName
616 us
<- liftMaybe ourName
617 ourUInfo
<- mgetUInfo us
618 selUInfo
<- mgetUInfo sel
619 let accesses
= map (uncurry getAccessInfo
) [(ourUInfo
,selUInfo
),(selUInfo
,ourUInfo
)]
620 let posLeft
= scoresPos
+^ hw
+^ neg hu
621 let posRight
= posLeft
+^
3*^hu
622 size
<- snd <$> (lift
.lift
) getGeom
624 lift
.renderToMain
$ renderStrColAbove
(brightish white
) "ESTEEM" scoresPos
625 lift
$ sequence_ [ registerSelectable
(scoresPos
+^v
) 0 SelRelScore | v
<- [hv
, hv
+^hu
] ]
626 drawRelScore sel scoresPos
627 fillArea
(posLeft
+^hw
) (map (posLeft
+^
) [zero
,hw
,neg hv
])
628 [ \pos
-> lift
(registerSelectable pos
0 (SelScoreLock
(Just sel
) accessed
$ ActiveLock us i
)) >>
629 drawNameWithCharAndCol us white
(lockIndexChar i
) col pos
631 , let accessed
= head accesses
!! i
633 | accessed
== Just AccessedPub
= dim pubColour
634 |
maybe False winsPoint accessed
= dim
$ scoreColour
$ -3
635 |
otherwise = obscure
$ scoreColour
3 ]
636 fillArea
(posRight
+^hw
) (map (posRight
+^
) [zero
,hw
,neg hv
])
637 [ \pos
-> lift
(registerSelectable pos
0 (SelScoreLock Nothing accessed
$ ActiveLock sel i
)) >>
638 drawNameWithCharAndCol sel white
(lockIndexChar i
) col pos
640 , let accessed
= accesses
!! 1 !! i
642 | accessed
== Just AccessedPub
= obscure pubColour
643 |
maybe False winsPoint accessed
= dim
$ scoreColour
3
644 |
otherwise = obscure
$ scoreColour
$ -3 ]
645 (posScore
,negScore
) <- MaybeT
$ (snd<$>) <$> getRelScoreDetails sel
646 lift
.lift
$ sequence_
648 renderToMain
$ renderStrColAt
(scoreColour score
) (sign
:show (abs score
)) pos
649 registerSelectable pos
0 SelRelScoreComponent
650 |
(sign
,score
,pos
) <-
651 [ ('-',-negScore
,posLeft
+^neg hv
+^hw
)
652 , ('+',posScore
,posRight
+^neg hv
+^hw
) ] ]
655 drawShortMouseHelp mode s
= do
656 mwhs
<- gets
$ whsButtons
.uiOptions
657 showBT
<- gets
(showButtonText
. uiOptions
)
658 when (showBT
&& isNothing mwhs
) $ do
659 let helps
= shortMouseHelp mode s
660 smallFont
<- gets dispFontSmall
661 renderToMain
$ withFont smallFont
$ sequence_
662 [ renderStrColAtLeft
(dim white
) help
663 (periphery
3 +^ neg hu
+^
(2-n
)*^hv
)
664 |
(n
,help
) <- zip [0..] helps
]
666 shortMouseHelp IMPlay PlayState
{ psTutLevel
= tutLevel
} =
667 [ "LMB: select/move tool"
668 , "LMB+drag: move tool" ] ++
670 |
not $ wrenchOnlyTutLevel tutLevel
] ++
671 [ "RMB+Wheel: undo/redo"
672 |
not $ noUndoTutLevel tutLevel
] ++
674 |
isNothing tutLevel
]
675 shortMouseHelp IMEdit _
=
676 [ "LMB: paint; Ctrl+LMB: delete"
677 , "Wheel: set paint type"
678 , "RMB: select piece; drag to move"
679 , "RMB+LMB: wait; RMB+MMB: delete piece"
680 , "MMB+Wheel: undo/redo"
682 shortMouseHelp IMReplay _
=
683 [ "Wheel: advance/regress time" ]
684 shortMouseHelp _ _
= []
686 -- waitEvent' : copy of SDL.Events.waitEvent, with the timeout increased
687 -- drastically to reduce CPU load when idling.
688 waitEvent
' :: IO Event
690 where loop
= do pumpEvents
693 NoEvent
-> threadDelay
10000 >> loop
701 getEventsTimeout us
= do
702 es
<- maybeToList <$> timeout us waitEvent
'
706 updateHoverStr
:: InputMode
-> UIM
()
707 updateHoverStr mode
= do
708 p
@(mPos
,isCentral
) <- gets mousePos
709 showBT
<- gets
(showButtonText
. uiOptions
)
710 hstr
<- runMaybeT
$ msum
711 [ MaybeT
( cmdAtMousePos p mode Nothing
) >>= lift
. describeCommandAndKeys
712 , guard showBT
>> MaybeT
(helpAtMousePos p mode
)
713 , guard (showBT
&& mode
== IMEdit
) >> msum
714 [ return $ "set paint mode: " ++ describeCommand
(paintTileCmds
!!i
)
715 | i
<- take (length paintTiles
) [0..]
716 , mPos
== paintButtonStart
+^ i
*^hv
]
717 , guard (mPos
== uiOptPos uiOB1
&& mode `
elem` uiOptModes uiOB1
) >> describeUIOptionButton uiOB1
718 , guard (mPos
== uiOptPos uiOB2
&& mode `
elem` uiOptModes uiOB2
) >> describeUIOptionButton uiOB2
719 , guard (mPos
== uiOptPos uiOB3
&& mode `
elem` uiOptModes uiOB3
) >> describeUIOptionButton uiOB3
720 , guard (mPos
== uiOptPos uiOB4
&& mode `
elem` uiOptModes uiOB4
) >> describeUIOptionButton uiOB4
721 , guard (mPos
== uiOptPos uiOB5
&& mode `
elem` uiOptModes uiOB5
) >> describeUIOptionButton uiOB5
723 , guard (mPos
== uiOptPos uiOB6
&& mode `
elem` uiOptModes uiOB6
) >> describeUIOptionButton uiOB6
726 modify
$ \ds
-> ds
{ hoverStr
= hstr
}
728 describeCommandAndKeys
:: Command
-> UIM
String
729 describeCommandAndKeys cmd
= do
730 uibdgs
<- gets
(Map
.findWithDefault
[] mode
. uiKeyBindings
)
731 return $ describeCommand cmd
++ " ["
733 (map showKeyFriendly
$ findBindings
(uibdgs
++ bindings mode
) cmd
)
737 fillArea
:: HexVec
-> [HexVec
] -> [HexVec
-> MainStateT UIM
()] -> MainStateT UIM
()
738 fillArea centre area draws
= do
739 offset
<- gets listOffset
741 listButton cmd
= \pos
-> lift
$ registerButton pos cmd
3 []
742 draws
' = if offset
> 0 && length draws
> na
743 then listButton CmdPrevPage
:
744 drop (max 0 $ min (length draws
- (na
-1)) (na
-1 + (na
-2)*(offset
-1))) draws
746 selDraws
= if length draws
' > na
747 then take (na
-1) draws
' ++ [listButton CmdNextPage
]
749 mapM_ (uncurry ($)) (
750 zip selDraws
$ sortBy (compare `on` hexVec2SVec
37) $
751 take (length selDraws
) $ sortBy
752 (compare `on`
(hexLen
. (-^centre
)))
755 drawOldLock ls pos
= void
.runMaybeT
$ msum [ do
757 lift
.lift
$ drawMiniLock lock pos
758 , lift
.lift
.renderToMain
$
759 renderStrColAt messageCol
(show ls
) pos
763 drawName
,drawNullName
:: Codename
-> HexVec
-> MainStateT UIM
()
764 drawName name pos
= nameCol name
>>= lift
. drawNameCol name pos
765 drawNullName name pos
= lift
. drawNameCol name pos
$ invisible white
767 drawNameCol name pos col
= renderToMain
$ do
768 drawAtRel
(playerGlyph col
) pos
769 renderStrColAt buttonTextCol name pos
771 drawRelScore name pos
= do
773 relScore
<- getRelScore name
774 flip (maybe (return ())) relScore
$ \score
->
776 renderToMain
$ renderStrColAt col
777 ((if score
> 0 then "+" else "") ++ show score
) pos
778 registerSelectable pos
0 SelRelScore
780 drawNote note pos
= case noteBehind note
of
781 Just al
-> drawActiveLock al pos
782 Nothing
-> drawPublicNote
(noteAuthor note
) pos
783 drawActiveLock al
@(ActiveLock name i
) pos
= do
784 accessed
<- accessedAL al
785 drawNameWithChar name
786 (if accessed
then accColour
else white
)
787 (lockIndexChar i
) pos
788 drawPublicNote name
=
789 drawNameWithChar name pubColour
'P
'
790 drawNameWithChar name charcol char pos
= do
792 drawNameWithCharAndCol name charcol char col pos
793 drawNameWithCharAndCol
:: String -> Pixel
-> Char -> Pixel
-> HexVec
-> MainStateT UIM
()
794 drawNameWithCharAndCol name charcol char col pos
= do
795 size
<- fi
.snd <$> lift getGeom
796 let up
= FVec
0 $ 1/2 - ylen
797 let down
= FVec
0 ylen
798 smallFont
<- lift
$ gets dispFontSmall
799 lift
.renderToMain
$ do
800 drawAtRel
(playerGlyph col
) pos
802 renderStrColAt buttonTextCol name pos
803 displaceRender down
$ withFont smallFont
$
804 renderStrColAt charcol
[char
] pos
806 pubColour
= colourWheel pubWheelAngle
-- ==purple
809 ourName
<- gets
((authUser
<$>) . curAuth
)
810 relScore
<- getRelScore name
811 return $ dim
$ case relScore
of
812 Nothing
-> Pixel
$ if ourName
== Just name
then 0xc0c0c000 else 0x80808000
813 Just score
-> scoreColour score
814 scoreColour
:: Int -> Pixel
815 scoreColour score
= Pixel
$ case score
of
824 drawLockInfo
:: ActiveLock
-> Maybe LockInfo
-> MainStateT UIM
()
825 drawLockInfo al
@(ActiveLock name idx
) Nothing
= do
826 let centre
= hw
+^neg hv
+^
7*(idx
-1)*^hu
827 lift
$ drawEmptyMiniLock centre
828 drawNameWithCharAndCol name white
(lockIndexChar idx
) (invisible white
) centre
829 ourName
<- gets
((authUser
<$>) . curAuth
)
830 lift
$ registerSelectable centre
3 $ SelLockUnset
(ourName
== Just name
) al
831 drawLockInfo al
@(ActiveLock name idx
) (Just lockinfo
) = do
832 let centre
= locksPos
+^
7*(idx
-1)*^hu
833 let accessedByPos
= centre
+^
3*^
(hv
+^ neg hw
)
834 let accessedPos
= centre
+^
2*^
(hw
+^ neg hv
)
835 let notesPos
= centre
+^
3*^
(hw
+^ neg hv
)
836 ourName
<- gets
((authUser
<$>) . curAuth
)
839 lock
<- mgetLock
$ lockSpec lockinfo
841 drawMiniLock lock centre
842 registerSelectable centre
3 $ SelLock al
844 drawActiveLock al centre
845 lift
$ registerSelectable centre
3 $ SelLock al
848 size
<- snd <$> lift getGeom
850 renderToMain
$ displaceRender
(FVec
1 0) $ renderStrColAt
(brightish white
) "UNLOCKED BY" $ accessedByPos
+^ hv
851 registerSelectable
(accessedByPos
+^ hv
) 0 SelPrivyHeader
852 registerSelectable
(accessedByPos
+^ hv
+^ hu
) 0 SelPrivyHeader
855 renderToMain
$ renderStrColAt pubColour
"All" accessedByPos
856 registerSelectable accessedByPos
1 SelPublicLock
857 else if null $ accessedBy lockinfo
858 then lift
.renderToMain
$ renderStrColAt dimWhiteCol
"No-one" accessedByPos
859 else fillArea accessedByPos
860 [ accessedByPos
+^ d | j
<- [0..2], i
<- [-2..3]
862 , let d
= j
*^hw
+^ i
*^hu
]
863 $ [ \pos
-> lift
(registerSelectable pos
0 (SelSolution note
)) >> drawNote note pos
864 | note
<- lockSolutions lockinfo
] ++
865 [ \pos
-> lift
(registerSelectable pos
0 (SelAccessed name
)) >> drawName name pos
866 | name
<- accessedBy lockinfo
\\ map noteAuthor
(lockSolutions lockinfo
) ]
868 undecls
<- gets undeclareds
869 case if isJust $ guard . (|| public lockinfo
) . (`
elem`
map noteAuthor
(lockSolutions lockinfo
)) =<< ourName
870 then if public lockinfo
871 then Just
(pubColour
,"Accessed!",AccessedPublic
)
872 else Just
(accColour
, "Solved!",AccessedSolved
)
873 else if any (\(Undeclared _ ls _
) -> ls
== lockSpec lockinfo
) undecls
874 then Just
(yellow
, "Undeclared",AccessedUndeclared
)
877 Just
(col
,str
,selstr
) -> lift
$ do
878 renderToMain
$ renderStrColAt col str accessedPos
879 registerSelectable accessedPos
1 (SelAccessedInfo selstr
)
881 read <- take 3 <$> getNotesReadOn lockinfo
882 unless (ourName
== Just name
) $ do
883 let readPos
= accessedPos
+^
(-3)*^hu
884 lift
.renderToMain
$ renderStrColAt
(if length read == 3 then accColour
else dimWhiteCol
)
886 when (length read == 3) $ lift
$ registerSelectable readPos
0 (SelAccessedInfo AccessedReadNotes
)
887 fillArea
(accessedPos
+^neg hu
) [ accessedPos
+^ i
*^hu | i
<- [-1..1] ]
888 $ take 3 $ [ \pos
-> lift
(registerSelectable pos
0 (SelReadNote note
)) >> drawNote note pos
889 | note
<- read ] ++ repeat (\pos
-> lift
$ registerSelectable pos
0 SelReadNoteSlot
>>
890 renderToMain
(drawAtRel
(HollowGlyph
$ dim green
) pos
))
893 renderToMain
$ displaceRender
(FVec
1 0) $ renderStrColAt
(brightish white
) "SECURING" $ notesPos
+^ hv
894 registerSelectable
(notesPos
+^ hv
) 0 SelNotesHeader
895 registerSelectable
(notesPos
+^ hv
+^ hu
) 0 SelNotesHeader
896 if null $ notesSecured lockinfo
897 then lift
.renderToMain
$ renderStrColAt dimWhiteCol
"None" notesPos
898 else fillArea notesPos
899 [ notesPos
+^ d | j
<- [0..2], i
<- [-2..3]
901 , let d
= j
*^hw
+^ i
*^hu
]
902 [ \pos
-> lift
(registerSelectable pos
0 (SelSecured note
)) >> drawActiveLock
(noteOn note
) pos
903 | note
<- notesSecured lockinfo
]
905 drawBasicHelpPage
:: (String,Pixel
) -> ([String],Pixel
) -> RenderM
()
906 drawBasicHelpPage
(title
,titleCol
) (body
,bodyCol
) = do
908 let startPos
= hv
+^
(length body `
div`
4)*^
(hv
+^neg hw
)
909 renderStrColAtCentre titleCol title
$ startPos
+^ hv
+^neg hw
911 [ renderStrColAtCentre bodyCol str
$
913 +^
(y`
div`
2)*^
(hw
+^neg hv
)
915 |
(y
,str
) <- zip [0..] body
]