Files
loop/src/Dodge/DisplayInventory.hs
T

327 lines
12 KiB
Haskell

-- {-# OPTIONS_GHC -fno-full-laziness #-}
--{-# LANGUAGE TupleSections #-}
{-# LANGUAGE LambdaCase #-}
--{-# LANGUAGE BangPatterns #-}
module Dodge.DisplayInventory (
updateInventoryPositioning,
updateCombinePositioning,
toggleCombineInv,
) where
import Control.Applicative
import Control.Lens
import Control.Monad
import Data.IntMap.Merge.Strict
import qualified Data.IntMap.Strict as IM
import Data.Maybe
import Data.Monoid
import Dodge.Base.You
import Dodge.CharacterEnums
import Dodge.Combine
import Dodge.Data.Combine
import Dodge.Data.Config
import Dodge.Data.HUD
import Dodge.Data.SelectionList
import Dodge.Data.Universe
import Dodge.Inventory
import Dodge.Inventory.CheckSlots
import Dodge.Inventory.SelectionList
import Dodge.Item.Display
import Dodge.Item.Grammar
import Dodge.ListDisplayParams
import Dodge.SelectionList
import Dodge.SelectionSections
import qualified ListHelp as List
import NewInt
import Picture.Base
updateCombinePositioning :: Universe -> Universe
updateCombinePositioning u =
u
& uvWorld . hud . subInventory . ciSections
%~ updateCombineSections (_uvWorld u) (_uvConfig u)
& uvWorld . hud . subInventory %~ checkCombineSelectionExists
updateCombineSections
:: World -> Config -> IM.IntMap (SelSection CombItem) -> IM.IntMap (SelSection CombItem)
updateCombineSections w cfig =
updateSectionsPositioning
(const 0)
(w ^? hud . subInventory . ciSelection . _Just)
(getAvailableListLines secondColumnLDP cfig)
(IM.fromDistinctAscList [(-1, sfclose), (0, sclose')])
where
filtcurs = w ^? hud . subInventory . ciSelection . _Just . slSec == Just (-1)
(sfclose, sclose) =
filterSectionsPair
filtcurs
(flip . andOrRegex $ regexCombs invitms)
(IM.fromDistinctAscList . zip [0 ..] $ combineList w)
"POSSIBLE COMBINATIONS"
$ w ^? hud . subInventory . ciFilter . _Just
invitms = _unNIntMap $ fmap (\k -> w ^?! cWorld . lWorld . items . ix k) $ fold $ w ^? cWorld . lWorld . creatures . ix 0 . crInv
sclose'
| null sclose =
IM.singleton 0 $
SelItem ["NONE"] 1 25 False white 2 Nothing
| otherwise = sclose
regexCombs :: IM.IntMap Item -> SelectionItem CombItem -> String -> Bool
regexCombs inv ci = \case
'#' : str -> any (g str) (_ciInvIDs $ fromJust $ _siPayload ci)
str -> (regexList str . _siPictures) ci
where
g str i = maybe False (regexList str . basicItemDisplay) (inv ^? ix i)
andOrRegex :: (a -> String -> Bool) -> a -> String -> Bool
andOrRegex f x = all (orRegex f x) . List.wordsBy '&'
orRegex :: (a -> String -> Bool) -> a -> String -> Bool
orRegex f x str = case words str of
[] -> True
xs -> any (f x) xs
updateInventoryPositioning :: Universe -> Universe
updateInventoryPositioning u =
u & uvWorld . hud . diSections
%~ updateDisplaySections (_uvWorld u) (_uvConfig u)
& uvWorld
%~ checkInventorySelectionExists
checkInventorySelectionExists :: World -> World
checkInventorySelectionExists w
| isJust $ w ^? hud . diSections . ix i . ssItems . ix j = w
| otherwise = scrollAugNextInSection w
where
Sel i j _ = fromMaybe (Sel 1 (-1) mempty) $ w ^? hud . diSelection . _Just
checkCombineSelectionExists :: SubInventory -> SubInventory
checkCombineSelectionExists si
| Just sss <- si ^? ciSections
, Sel i j _ <- fromMaybe (Sel 0 0 mempty) $ si ^? ciSelection . _Just
, isNothing $ si ^? ciSections . ix i . ssItems . ix j =
si & ciSelection ?~ Sel 0 (-1) mempty
& ciSelection
%~ scrollSelectionSections (-1) sss
| otherwise = si
displayIndents :: Int -> Int
displayIndents 0 = 2
displayIndents 3 = 2
displayIndents 5 = 2
displayIndents _ = 0
updateDisplaySections :: World -> Config -> IMSS () -> IMSS ()
updateDisplaySections w cfig =
updateSectionsPositioning
displayIndents
mselpos
(getAvailableListLines invDP cfig)
$ IM.fromDistinctAscList $
zip [-1 .. 5]
[ sfinv
, sinv
, IM.singleton 0 $
SelItem [displayFreeSlots (crNumFreeSlots (w ^. cWorld . lWorld . items) cr)] 1 15 True invDimColor 2 Nothing
, sfclose
, sclose
, interfaceshead
, btitems
]
where
mselpos = w ^? hud . diSelection . _Just
invfiltcurs = mselpos ^? _Just . slSec == Just (-1)
(sfinv, sinv) =
filterSectionsPair invfiltcurs plainRegex invitems "INVENTORY" $
w ^? hud . diInvFilter . _Just
closefiltcurs = mselpos ^? _Just . slSec == Just 2
(sfclose, sclose) =
filterSectionsPair closefiltcurs plainRegex closeitms "NEARBY ITEMS" $
w ^? hud . diCloseFilter . _Just
interfaceshead = if null btitems then mempty else makehead "NEARBY INTERFACES"
btitems =
IM.fromDistinctAscList . zip [0 ..] $
mapMaybe (closeButtonToSelectionItem w) (w ^. hud . closeButtons)
makehead str = IM.singleton 0 $ SelItem [str] 1 15 False white 0 Nothing
cr = you w
closeitms =
IM.fromDistinctAscList . zip [0 ..] $
mapMaybe (closeItemToSelectionItem w . _unNInt) (w ^. hud . closeItems)
invitems =
IM.map
(uncurry (invSelectionItem w))
(invIndents $ (\k -> w ^?! cWorld . lWorld . items . ix k) <$> _crInv cr)
filterSectionsPair ::
Bool -> -- check for whether filter is in focus, changes string at the end
(String -> SelectionItem a -> Bool) ->
IM.IntMap (SelectionItem a) ->
String ->
Maybe String ->
(IM.IntMap (SelectionItem a), IM.IntMap (SelectionItem a))
filterSectionsPair infocus filtfn itms s mfilt = (x, itms')
where
filtcurs = if infocus then cFilledRect else cWireRect
x = IM.singleton 0 $
fromMaybe (SelItem [s] 1 (length s) False white 0 Nothing) $ do
str <- mfilt
return $
SelItem
[s ++ " FILTER/" ++ str ++ [filtcurs], numfiltitems]
2
(length (s ++ " FILTER/" ++ str ++ [filtcurs]))
True
white
0
Nothing
f y = y
-- | null y =
-- IM.singleton 0 $ SelItem ["(NONE)"] 1 (length s) True (greyN 0.5) 0 Nothing
-- | otherwise = y
itms' = f $ maybe id (IM.filter . filtfn) mfilt itms
numfiltitems = show (length itms - length itms') ++ " FILTERED"
invDimColor :: Color
invDimColor = greyN 0.7
plainRegex :: String -> SelectionItem a -> Bool
plainRegex = flip $ andOrRegex (\si str -> regexList str (_siPictures si))
displayFreeSlots :: Int -> String
displayFreeSlots x = case x of
0 -> "INVENTORY FULL"
1 -> "1 FREE SLOT"
_ -> show x ++ " FREE SLOTS"
sectionsDesiredLines :: IM.IntMap (IM.IntMap (SelectionItem a)) -> IM.IntMap Int
sectionsDesiredLines = fmap $ alaf Sum foldMap _siHeight
sectionsSizes ::
Int -> -- total available lines
[Int] -> -- ordering in which to consider sections (others to be done lowest to highest)
IM.IntMap Int -> -- desired lines
IM.IntMap Int
sectionsSizes x is sss = doSectionSize extraavailable mintaken is sss mempty
where
mintaken = IM.mapWithKey (min . const 10) sss
extraavailable = x - sum mintaken
doSectionSize ::
Int -> -- extra available lines
IM.IntMap Int -> -- minimum line bounds for each section
[Int] -> -- ordering in which to consider sections (others to be done lowest to highest)
IM.IntMap Int -> -- desired lines in sections not yet considered
IM.IntMap Int -> -- sections already sized
IM.IntMap Int
doSectionSize extraavailable mintaken is sss done = fromMaybe done $ do
(v, others, k, ks) <- getsecusingis <|> getminsec
minv <- mintaken ^? ix k
let extradesired = v - minv
linesgiven = min extraavailable extradesired
return $
doSectionSize (extraavailable - linesgiven) mintaken ks others $
done
& at k ?~ linesgiven + minv
where
getsecusingis = do
(k, ks) <- List.uncons is
let (my, s') = IM.updateLookupWithKey (\_ _ -> Nothing) k sss
y <- my
return (y, s', k, ks)
getminsec = do
((k, v), s') <- IM.minViewWithKey sss
return (v, s', k, [])
updateSectionsPositioning ::
(Int -> Int) -> -- for determining each sections indent
Maybe Selection ->
Int ->
IM.IntMap (IM.IntMap (SelectionItem a)) ->
IM.IntMap (SelSection a) ->
IM.IntMap (SelSection a)
updateSectionsPositioning h mselpos allavailablelines lsss sss =
IM.intersectionWithKey (\k -> updateSection (h k) (m k)) ls ssizes `g` offsets
where
offsets = fmap _ssOffset sss
m k = do
Sel k' i _ <- mselpos
guard $ k == k'
return i
ls = lsss
-- defaults non-existing offsets to 0
g = merge (mapMissing (const ($ 0))) dropMissing (zipWithMatched (const ($)))
lk = mselpos ^.. _Just . slSec
ssizes = sectionsSizes allavailablelines lk $ sectionsDesiredLines ls
updateSection ::
Int ->
Maybe Int ->
IM.IntMap (SelectionItem a) ->
Int ->
Int ->
SelSection a
updateSection indent mcsel sis availablelines oldoffset =
SelSection
{ _ssItems = sis
, _ssOffset = offset
, _ssShownItems = shownitems
, _ssShownLength = min aslength availablelines
, _ssIndent = indent
}
where
shownitems = tweakfirst . tweaklast $ take availablelines shownstrings
oldoffsetbounded = max 0 . min oldoffset $ aslength - availablelines
offset = fromMaybe oldoffsetbounded $ do
csel <- mcsel
maxcsel <- fst <$> IM.lookupMax sis
xselsize <- sis ^? ix csel . siHeight
return $ case sum $ fmap _siHeight . fst . IM.split csel $ sis of
pos | pos == 0 || aslength <= availablelines -> 0
pos | pos - 1 < oldoffset -> pos - 1
pos | maxcsel == csel -> pos - availablelines + xselsize
pos
| pos + 1 + xselsize - availablelines > oldoffset ->
pos - availablelines + 1 + xselsize
_
| aslength - availablelines < oldoffset ->
aslength - availablelines
_ -> oldoffsetbounded
tweakfirst
| offset > 0 = ix 0 .~ color white (text (replicate 15 (toEnum 30)))
| otherwise = id
tweaklast
| aslength - offset > availablelines =
ix (availablelines - 1)
.~ color white (text . replicate 15 $ toEnum 31)
| otherwise = id
(allstrings, Sum aslength) = foldMap listSelectionColorPicture sis
shownstrings = drop offset allstrings
listSelectionColorPicture :: SelectionItem a -> ([Picture], Sum Int)
listSelectionColorPicture si = (g <$> _siPictures si, Sum $ _siHeight si)
where
g = translate lindent 0 . color (_siColor si) . text
lindent = 100 * fromIntegral (_siOffX si)
regexList :: String -> [String] -> Bool
regexList = any . List.isInfixOf
toggleCombineInv :: Universe -> Universe
toggleCombineInv uv =
uv & case uv ^? uvWorld . hud . subInventory of
Just CombineInventory{} -> uvWorld . hud . subInventory .~ NoSubInventory
_ -> uvWorld %~ enterCombineInv (uv ^. uvConfig)
enterCombineInv :: Config -> World -> World
enterCombineInv cfig w =
w & hud . subInventory
.~ CombineInventory
{ _ciSections = updateCombineSections w cfig mempty
, _ciSelection = Just (Sel 0 0 mempty)
, _ciFilter = Nothing
}
& hud . diInvFilter
.~ Nothing