Files
loop/src/Dodge/Render/HUD.hs
T

508 lines
18 KiB
Haskell

--{-# LANGUAGE TupleSections #-}
module Dodge.Render.HUD (
drawHUD,
) where
import Dodge.Creature.Info
import Control.Lens
import qualified Data.Map.Strict as M
import Data.Maybe
import qualified Data.Vector as V
import Dodge.Base
import Dodge.Clock
import Dodge.Combine
import Dodge.Data.CardinalPoint
import Dodge.Data.Config
import Dodge.Data.SelectionList
import Dodge.Data.World
import Dodge.Inventory
import Dodge.Inventory.ItemSpace
import Dodge.Inventory.SelectionList
import Dodge.Item.Display
import Dodge.Item.Info
import Dodge.Render.Connectors
import Dodge.Render.List
import Dodge.Tweak.Show
import Dodge.WorldPos
import Geometry
import qualified IntMapHelp as IM
import Justify
import ListHelp
import Padding
import Picture
import SDL (MouseButton (..))
drawHUD :: Configuration -> World -> Picture
drawHUD cfig w = case w ^. hud . hudElement of
DisplayCarte -> drawCarte cfig w
DisplayInventory subinv ->
drawInGameHUD sl cfig w
<> drawSubInventory subinv cfig w
where
sl = makeInventorySelectionList w
drawInGameHUD :: SelectionList () -> Configuration -> World -> Picture
drawInGameHUD sl cfig w =
pictures
[ winScale cfig . dShadCol white $ displayHP 0 cfig w
, inventoryDisplay w cfig sl
]
defaultListDisplayParams :: ListDisplayParams
defaultListDisplayParams =
ListDisplayParams
{ _ldpVerticalGap = 10
, _ldpScale = 1
, _ldpPosX = 0
, _ldpPosY = 0
, _ldpCursorType = NoCursor
, _ldpWidth = FixedSelectionWidth 15
}
defaultSubInvSelectionList :: SelectionList a
defaultSubInvSelectionList =
SelectionList
{ _slItems = []
, _slSelPos = Nothing
, _slLength = 15
, _slRegex = ""
, _slRegexInput = False
}
invDisplayParams :: World -> ListDisplayParams
invDisplayParams w =
defaultListDisplayParams
-- & ldpWidth .~ FixedSelectionWidth topInvW
& ldpCursorType .~ BorderCursor selcursortype
& ldpWidth .~ FixedSelectionWidth (determineInvSelCursorWidth w)
where
selcursortype
| isexaminesub || ButtonRight `M.member` _mouseButtons (_input w) = [North, South, East, West]
| otherwise = [North, South, West]
isexaminesub = case w ^? hud . hudElement . subInventory of
Just ExamineInventory{} -> True
_ -> False
inventoryDisplay :: World -> Configuration -> SelectionList () -> Picture
inventoryDisplay = drawSelectionList . invDisplayParams
drawSubInventory :: SubInventory -> Configuration -> World -> Picture
drawSubInventory subinv cfig w = case subinv of
LockedInventory -> mempty -- topInvCursor col cursPos cfig w
NoSubInventory -> drawNoSubInventory cfig w
ExamineInventory mtweaki -> drawExamineInventory cfig mtweaki w
DisplayTerminal tid -> displayTerminal tid cfig (w ^. cWorld . lWorld)
CombineInventory mi regex x ->
titledSub
cfig
("COMBINE")
(combineListSelection w mi regex x)
<> combineInventoryExtra mi cfig w
titledSub :: Configuration -> String -> SelectionList a -> Picture
titledSub cfig subtitle subitems =
invHead cfig subtitle
<> drawSelectionList secondColumnParams cfig subitems
drawExamineInventory :: Configuration -> Maybe Int -> World -> Picture
drawExamineInventory cfig mtweaki w =
titledSub
cfig
"EXAMINE"
(defaultSubInvSelectionList & slItems .~ ammoTweakSelectionItems itm
++ map f (makeParagraph 60 $ yourAugmentedItem itemInfo (yourInfo (you w)) (closeObjectInfo (crNumFreeSlots (you w)) ) w))
<> examineInventoryExtra mtweaki itm cfig
where
itm = yourItem w
f str =
SelectionItem
{ _siPictures = [str]
, _siHeight = 1
, _siIsSelectable = True
, _siWidth = length str
, _siColor = white
, _siOffX = 0
, _siPayload = ()
}
closeObjectInfo :: Int -> Either FloorItem Button -> String
closeObjectInfo n x = case x of
Left FlIt{_flIt = itm} -> itemInfo itm ++ " It is on the floor" ++ floorItemPickupInfo n itm
Right _ -> "Some sort of switch or button."
floorItemPickupInfo :: Int -> Item -> String
floorItemPickupInfo n itm
| n >= ceiling (_itInvSize itm) = ", but you have space to pick it up."
| otherwise = ", and you don't have space to pick it up."
yourAugmentedItem :: (Item -> a) -> a -> (Either FloorItem Button -> a) -> World -> a
yourAugmentedItem f x g w
| i < n = f $ yourInv w ^?! ix i
| i == n = x
| otherwise = g $ w ^?! hud . closeObjects . ix (i - n - 1)
where
i = crSel (you w)
n = length $ yourInv w
drawNoSubInventory :: Configuration -> World -> Picture
drawNoSubInventory cfig w =
pictures
[ closeobjectcursor
, equipcursors
, equipcursor -- the order is important, this should go on top of the other equipcursors
, rboptions
]
where
closeobjectcursor = case selectedCloseObject w of
Nothing -> mempty
Just (i, _) ->
drawCursorAt
(invDisplayParams w & ldpCursorType .~ BorderCursor [North, South])
cfig
(Just i)
(inventorySelectionList w)
equipcursor = case cr ^. crLeftInvSel . lisMPos of
Just invid -> f cyan invid (_crInvEquipped cr IM.! invid)
_ -> mempty
equipcursors = IM.foldMapWithKey (f yellow) (_crInvEquipped cr)
cr = you w
rboptions
| ButtonRight `M.member` _mouseButtons (_input w) = drawRBOptions cfig w (_rbOptions w)
| otherwise = mempty
f col invid epos = listTextPictureAt 144 0 cfig (selNumPos invid w) . color col $ text $ eqPosText epos
examineInventoryExtra :: Maybe Int -> Maybe Item -> Configuration -> Picture
examineInventoryExtra mtweaki mitm cfig =
pictures
[ fromMaybe mempty $ do
tweaki <- mtweaki
-- consider moving this functionality out into a tweaks module
tparam <- mitm ^? _Just . itTweaks . tweakParams . ix tweaki
return $ listCursorNSW subInvX 60 cfig tweaki white (length $ showTweak tparam) 15
-- , drawSelectionList thirdColumnParams cfig (thirdColumnPara (itmInfo mitm))
]
combineInventoryExtra :: Maybe Int -> Configuration -> World -> Picture
combineInventoryExtra mi cfig w =
pictures
[ fromMaybe mempty $ do
i <- mi
cpos <- combinePoss w !? i
col <- (combineItemListYou w !? i) <&> _itInvColor . snd
return $
pictures
[ fromMaybe mempty $ do
strs <- fmap (fst . snd) (combineListInfo w !? i)
return $ listPicturesAtOff (subInvX + 150) 60 cfig cpos $ map (color red . text) strs
, fromMaybe mempty $ do
lnks <- map fst (combineItemListYou w) !? i
return $
lnkMidPosInvSelsCol cfig w cpos col lnks
<> foldMap (topCursorTypeWidth listCursorNESW (topInvW + 2) cfig w) lnks
<> combineCounts cfig w lnks
]
]
secondColumnParams :: ListDisplayParams
secondColumnParams =
defaultListDisplayParams
& ldpPosX .~ subInvX
& ldpPosY .~ 60
& ldpCursorType .~ BorderCursor [North, South, West]
--thirdColumnParams :: ListDisplayParams
--thirdColumnParams =
-- defaultListDisplayParams
-- & ldpPosX .~ 9 * fromIntegral topInvW + 275
-- & ldpPosY .~ 60
--thirdColumnPara :: [String] -> SelectionList ()
--thirdColumnPara strs = SelectionList (map f strs) Nothing (length strs)
-- where
-- f str =
-- SelectionItem
-- { _siPictures = [text str]
-- , _siHeight = 1
-- , _siIsSelectable = True
-- , _siWidth = length str
-- , _siColor = white
-- , _siOffX = 0
-- , _siPayload = ()
-- }
displayTerminal :: Int -> Configuration -> LWorld -> Picture
displayTerminal tid cfig w = fromMaybe mempty $ do
tm <- w ^? terminals . ix tid
return $
pictures
[ invHead cfig (_tmTitle tm ++ ":T" ++ show tid)
, drawSelectionList secondColumnParams cfig (thesellist tm)
]
where
toselitm (str, col) = SelectionItem [str] 1 True (length str) col 0 ()
thesellist tm = SelectionList (thelist tm) Nothing (length (thelist tm)) "" False
thelist tm =
map toselitm . displayTermInput tm
. reverse
. take (_tmMaxLines tm)
$ _tmDisplayedLines tm
displayTermInput tm = case _tmInput tm of
TerminalInput s hasfoc _ -> (++ [(displayInputText tm s ++ displayBlinkCursor hasfoc, white)])
displayInputText tm s
| _tmStatus tm == TerminalReady = '>' : s
| otherwise = ""
displayBlinkCursor hasfoc
| hasfoc = clockCycle 10 (V.fromList ["_", ""]) w
| otherwise = []
drawRBOptions :: Configuration -> World -> RightButtonOptions -> Picture
drawRBOptions cfig w EquipOptions{_opEquip = es, _opSel = i, _opAllocateEquipment = ae} =
listPicturesAtOff 342 0 cfig (curpos - i) (map (text . eqPosText) es)
<> case ae of
DoNotMoveEquipment -> mempty
PutOnEquipment{} ->
midtext "PUT ONTO"
MoveEquipment{} ->
midtext "MOVE TO" <> extratext []
SwapEquipment{_allocOldPos = oldp, _allocSwapID = sid} ->
midtext "MOVE TO" <> extratext ("SWAPS " ++ otheritem sid ++ " ONTO " ++ eqPosText oldp)
ReplaceEquipment{_allocRemoveID = rid} ->
midtext "PUT ONTO" <> extratext ("REMOVES " ++ otheritem rid)
RemoveEquipment{} ->
midtext "TAKE OFF"
where
midtext str = listTextPictureAt 252 0 cfig curpos (text str)
extratext str = listTextPictureAt 432 0 cfig curpos (text (str ++ deactivatetext))
deactivatetext = case w ^? rbOptions . opActivateEquipment . deactivateEquipment of
Just k -> " DEACTIVATES " ++ show (_iyBase $ _itType (_crInv (you w) IM.! k))
Nothing -> ""
curpos = invSelPos w
otheritem j = show $ _iyBase $ _itType (_crInv (you w) IM.! j)
drawRBOptions _ _ _ = mempty
eqPosText :: EquipPosition -> String
eqPosText ep = case ep of
OnHead -> "HEAD"
OnChest -> "CHEST"
OnBack -> "BACK"
OnLeftWrist -> "L.WRIST"
OnRightWrist -> "R.WRIST"
OnLegs -> "LEGS"
OnSpecial -> "EQUIPPED"
topInvW :: Int
topInvW = 15
subInvX :: Float
subInvX = 9 * fromIntegral topInvW + 50
combineCounts :: Configuration -> World -> [Int] -> Picture
combineCounts cfig w = winScale cfig . foldMap f . group
where
f (i : is) = case yourInv w ^? ix i . itUse . useAmount of
Just _ -> color (selNumCol i w) $ uncurryV translate (selNumTextPos cfig w i) . scale 0.1 0.1 . text $ ('-' : show (length is + 1))
_ -> mempty
f _ = mempty
lnkMidPosInvSelsCol :: Configuration -> World -> Int -> Color -> [Int] -> Picture
lnkMidPosInvSelsCol cfig w i col =
winScale cfig
. foldMap (\j -> zConnectCol rp (V2 18 0 + selNumMidHeight cfig w j) col white white (selNumCol j w))
where
rp = V2 (190 - hw) (hh - (20 * fromIntegral i + 77.5))
hh = halfHeight cfig
hw = halfWidth cfig
topCursorTypeWidth ::
(Float -> Float -> Configuration -> Int -> Color -> Int -> Int -> Picture) ->
Int ->
Configuration ->
World ->
Int ->
Picture
topCursorTypeWidth ctype width cfig w i =
ctype 0 0 cfig (selNumPos i w) (selNumCol i w) width (invSelSize i w)
determineInvSelCursorWidth :: World -> Int
determineInvSelCursorWidth w = case _rbOptions w of
NoRightButtonOptions -> topInvW
EquipOptions{}
| ButtonRight `M.member` _mouseButtons (_input w)
&& w ^? hud . hudElement . subInventory == Just NoSubInventory ->
47
| otherwise -> topInvW
combineListSelection :: World -> Maybe Int -> String -> Bool -> SelectionList ()
combineListSelection w mi regex x =
defaultSubInvSelectionList
& slItems .~ combineListSelectionItems w
& slSelPos .~ mi
& slRegex .~ regex
& slRegexInput .~ x
combineListSelectionItems :: World -> [SelectionItem ()]
combineListSelectionItems w = case combineListSelectionItems' w of
[] -> [SelectionItem [thetext] 1 False (length thetext) white 0 ()]
xs -> xs
where
thetext = "NO POSSIBLE COMBINATIONS"
combineListSelectionItems' :: World -> [SelectionItem ()]
combineListSelectionItems' = map (picsToSelectable 15 . itemText . snd) . combineItemListYou
ammoTweakSelectionItems :: Maybe Item -> [SelectionItem ()]
ammoTweakSelectionItems = textSelItems . ammoTweakStrings
ammoTweakStrings :: Maybe Item -> [String]
ammoTweakStrings it = case it ^? _Just . itTweaks . tweakParams of
Just l -> map tweakString $ IM.elems l
_ -> ["NOT TWEAKABLE"]
tweakString :: TweakParam -> String
tweakString tp = rightPad 12 ' ' (show $ _tweakType tp) ++ " " ++ showTweak tp
invHead :: Configuration -> String -> Picture
invHead cfig =
winScale cfig
. translate (- halfWidth cfig + subInvX + 20) (halfHeight cfig - 40)
. dShadCol white
. scale 0.4 0.4
. text
drawCarte :: Configuration -> World -> Picture
drawCarte cfig w =
pictures $
renderListAt 0 0 cfig locs :
zipWith bConnect (displayListEndCoords cfig locTexts) locPoss
++ mapOverlay cfig w
++ [mainListCursor white iPos cfig]
where
iPos = w ^. cWorld . lWorld . selLocation
locs = map (\(_, s) -> (s, white)) . IM.elems . _seenLocations . _lWorld $ _cWorld w
locPoss = map (cartePosToScreen cfig (w ^. hud) . ($ w) . doWorldPos . fst) . IM.elems . _seenLocations . _lWorld $ _cWorld w
locTexts = map fst locs
displayListEndCoords :: Configuration -> [String] -> [Point2]
displayListEndCoords cfig ss = map (doWindowScale cfig) $ zipWith h ss $ map f [1 ..]
where
f :: Int -> Point2
f i = V2 (15 - halfWidth cfig) (2.5 + halfHeight cfig - (20 * fromIntegral i))
h :: String -> Point2 -> Point2
h s (V2 x y) = V2 (x + 9 * fromIntegral (length s)) y
mapOverlay :: Configuration -> World -> [Picture]
mapOverlay cfig w =
(color (withAlpha 0.5 black) . polygon $ reverse $ rectNSWE 1 (-1) 1 (-1)) :
[foldMap (drawMapWall cfig (w ^. hud)) $ IM.restrictKeys (lw ^. walls) (w ^. cWorld . seenWalls)]
where
--(mapMaybe (mapWall cfig (w ^. hud)) . IM.elems $ w ^. cWorld . lWorld . walls)
lw = w ^. cWorld . lWorld
drawMapWall :: Configuration -> HUD -> Wall -> Picture
drawMapWall cfig thehud wl = color c . polygon $ map (cartePosToScreen cfig thehud) [x, x +.+ n2, y +.+ n2, y]
where
t = normalizeV (y -.- x)
n2 = 20 *.* vNormal t
(x, y) = _wlLine wl
c = _wlColor wl
--mapWall :: Configuration -> HUD -> Wall -> Maybe Picture
--mapWall cfig thehud wl
-- | _wlSeen wl = Just . color c . polygon $ map (cartePosToScreen cfig thehud) [x, x +.+ n2, y +.+ n2, y]
-- | otherwise = Nothing
-- where
-- t = normalizeV (y -.- x)
-- n2 = 20 *.* vNormal t
-- (x, y) = _wlLine wl
-- c = _wlColor wl
--selectedCloseObjectLink :: Configuration -> World -> Picture
--selectedCloseObjectLink cfig w = mempty
-- | Pictures of popup text for items close to your position.
--closeObjectTexts :: Configuration -> World -> Picture
--closeObjectTexts cfig w = pictures $
-- renderListAt pushout (negate 20 * fromIntegral invPos) cfig (map colAndText $ _closeObjects w)
-- : maybeToList maybeLine
-- where
-- colAndText (Left x) = ( _itName $ _flIt x, _itInvColor $ _flIt x)
-- colAndText (Right x) = (_btText x,yellow)
-- youSel = _crInvSel $ you w
-- freeSlot = mayIt >>= \it -> checkInvSlotsYou (_flIt it) w
-- invPos = fromMaybe youSel freeSlot
-- mayObj = listToMaybe $ _closeObjects w
-- mayIt = mayObj >>= maybeLeft
-- maybeLeft (Left x) = Just x
-- maybeLeft _ = Nothing
-- pushout = 120
-- objPos obj = case obj of
-- Left flit -> _flItPos flit
-- Right bt -> _btPos bt
-- mayScreenPos = fmap (worldPosToScreen w . objPos) mayObj
-- maybeLine = do
-- itScreenPos <- mayScreenPos
-- (theText,col) <- fmap colAndText mayObj
-- let textWidth = 9 * fromIntegral (length theText)
-- let p = V2 (textWidth + 15 + pushout - halfWidth cfig)
-- ( halfHeight cfig - 20* (fromIntegral invPos +1) + 2.5)
-- return . winScale cfig . color col $ lConnect p itScreenPos
mainListCursor :: Color -> Int -> Configuration -> Picture
mainListCursor c = openCursorAt 120 c 5 0
picsToSelectable :: Int -> [String] -> SelectionItem ()
picsToSelectable wdth pics =
SelectionItem
{ _siPictures = pics
, _siHeight = length pics
, _siIsSelectable = True
, _siWidth = wdth
, _siColor = white
, _siOffX = 0
, _siPayload = ()
}
textSelItems :: [String] -> [SelectionItem ()]
textSelItems = map (picsToSelectable 15 . (: []))
itemText :: Item -> [String]
{-# INLINE itemText #-}
itemText it = f $ case _itCurseStatus it of
UndroppableIdentified -> itemDisplay it
_ -> itemDisplay it
where
f = take (itSlotsTaken it) . (++ replicate 10 "*")
openCursorAt ::
-- | Width
Float ->
Color ->
-- | x offset
Float ->
-- | y offset
Float ->
-- | y offset (discrete)
Int ->
Configuration ->
Picture
openCursorAt wth col xoff yoff yint w =
winScale w
. translate (xoff - halfWidth w) (halfHeight w - (20 * fromIntegral yint + yoff) - 20)
$ lineCol
[ (V2 wth 12.5, withAlpha 0 col)
, (V2 0 12.5, col)
, (V2 0 (-7.5), col)
, (V2 wth (-7.5), withAlpha 0 col)
]
displayHP :: Int -> Configuration -> World -> Picture
displayHP cid cfig =
translate (halfWidth cfig -80) (halfHeight cfig -20)
. scale 0.2 0.2
. text
. leftPad 5 ' '
. show
. (^?! cWorld . lWorld . creatures . ix cid . crHP)