Files
loop/src/Dodge/Update/Input/InGame.hs
T
2025-01-06 12:23:59 +00:00

538 lines
20 KiB
Haskell

{-# LANGUAGE LambdaCase #-}
module Dodge.Update.Input.InGame (
updateUseInputInGame,
updateMouseInGame,
) where
import Control.Applicative
import Control.Monad
import Data.Foldable
import qualified Data.IntMap.Strict as IM
import qualified Data.IntSet as IS
import Data.List (sort)
import qualified Data.Map.Strict as M
import Data.Maybe
import Data.Monoid
import Dodge.Camera
import Dodge.Creature.Action
import Dodge.Data.Combine
import Dodge.Data.Universe
import Dodge.DisplayInventory
import Dodge.Event.Test
import Dodge.Inventory
import Dodge.Inventory.Add
import Dodge.Item.Display
import Dodge.Item.InvSize
import Dodge.ListDisplayParams
import Dodge.Menu
import Dodge.Save
import Dodge.SelectedClose
import Dodge.SelectionSections
import Dodge.SoundLogic
import Dodge.Terminal
import Dodge.Update.Input.DebugTest
import Dodge.Update.Input.Text
--import Dodge.WorldPos
--import Geometry
import LensHelp
import NewInt
import SDL
updateUseInputInGame :: Universe -> Universe
updateUseInputInGame u = updateFunctionKeys $ case u ^. uvWorld . hud . hudElement of
DisplayInventory{_subInventory = si, _diSelection = disel} -> case si of
DisplayTerminal tmid -> updateKeysInTerminal tmid u
CombineInventory{_ciSections = sss, _ciSelection = msel@(Just (-1, _, _))} ->
u
& uvWorld . hud . hudElement . subInventory
%~ docombineregexinput sss msel
& uvWorld . worldEventFlags . at CombineInventoryChange ?~ ()
_
| disel ^? _Just . _1 == Just (-1) ->
u
& uvWorld . hud . hudElement
%~ dodisplayregexinput diInvFilter diInvFilter (-1)
& uvWorld . worldEventFlags . at InventoryChange ?~ ()
& uvWorld %~ setInvPosFromSS
_
| disel ^? _Just . _1 == Just 2 ->
u
& uvWorld . hud . hudElement
%~ dodisplayregexinput diCloseFilter diCloseFilter 2
& uvWorld . worldEventFlags . at InventoryChange ?~ ()
& uvWorld %~ setInvPosFromSS
_ -> M.foldlWithKey' updateKeyInGame u pkeys
where
pkeys = u ^. uvWorld . input . pressedKeys
dodisplayregexinput filterprism filterlens x di = fromMaybe di $ do
sss <- di ^? diSections
msel <- di ^? diSelection
filts <- di ^? filterprism
let (sss', msel', filts') = doRegexInput (u ^. uvWorld . input) x sss msel filts
return $
di & diSections .~ sss'
& diSelection .~ msel'
& filterlens .~ filts'
docombineregexinput sss msel ci = fromMaybe ci $ do
filts <- ci ^? ciFilter
let (sss', msel', filts') = doRegexInput (u ^. uvWorld . input) (-1) sss msel filts
return $
ci & ciSections .~ sss'
& ciSelection .~ msel'
& ciFilter .~ filts'
updateMouseInGame :: Configuration -> World -> World
updateMouseInGame cfig w
| Just 0 <- lbpress = updateMouseClickInGame cfig w
| Just _ <- lbpress = updateMouseHeldInGame cfig w
| Just 0 <- lbrelease = updateMouseReleaseInGame w
| otherwise = w
where
lbpress = w ^? input . mouseButtons . ix ButtonLeft
lbrelease = w ^? input . mouseButtonsReleased . ix ButtonLeft
updateMouseHeldInGame :: Configuration -> World -> World
updateMouseHeldInGame cfig w = case w ^. input . mouseContext of
OverInvDragSelect sstart _ -> fromMaybe w $ do
sss <- w ^? hud . hudElement . diSections
let msel = inverseSelNumPos cfig invDP sss (w ^. input . mousePos)
guard $ Just (fst sstart) == fmap fst msel || isNothing msel
return $ w & input . mouseContext . mcoSelEnd .~ fmap snd msel
OverInvDrag k mmouseover ab bn -> doDrag 30 k mmouseover ab bn w
_ -> w
doDrag ::
Int ->
Int ->
Maybe (Int, Int) ->
Maybe (Int, Int) ->
Maybe (Int, Int) ->
World ->
World
doDrag 0 _ _ _ _ w = w
doDrag n k mmouseover ab bn w = fromMaybe w $ do
ss <- w ^? hud . hudElement . diSections . ix k . ssItems
x <- mmouseover
is <- w ^? hud . hudElement . diSelection . _Just . _3
return $
if concurrentIS is
then shiftInvItems n k x ab bn is ss w
else collectInvItems k is w
tryDropSelected :: Maybe (Int, Int) -> World -> Maybe World
tryDropSelected mpos w = do
guard $ maybe True (\(i, _) -> i == 3) mpos
cr <- w ^? cWorld . lWorld . creatures . ix 0
(0, _, xs) <- w ^? hud . hudElement . diSelection . _Just
return . foldl' (flip $ dropItem cr) w . IS.toDescList $ xs
tryPickupSelected :: Int -> Maybe (Int, Int) -> World -> Maybe World
tryPickupSelected k mpos w = do
guard $ k == 3
guard $ maybe True (\(i, _) -> i == 0 || i == 1) mpos
cr <- w ^? cWorld . lWorld . creatures . ix 0
let nfreeslots = crNumFreeSlots cr
xs <- w ^? hud . hudElement . diSelection . _Just . _3
let itmstopickup = mapMaybe g $ IS.toList xs
let slotsneeded = alaf Sum foldMap (itInvHeight . _flIt) itmstopickup
guard $ nfreeslots >= slotsneeded
return $ case mpos of
Just (0, j) ->
foldr (pickUpItemAt j 0) w itmstopickup
& hud . hudElement . diSelection
?~ ( 0
, j
, IS.fromDistinctAscList [j .. j + IS.size xs -1]
)
_ ->
foldl' (flip $ pickUpItem 0) w itmstopickup
& hud . hudElement . diSelection . _Just . _3 %~ const mempty
where
g i = do
NInt j <- w ^? hud . closeItems . ix i
w ^? cWorld . lWorld . floorItems . unNIntMap . ix j
updateMouseReleaseInGame :: World -> World
updateMouseReleaseInGame w = case w ^. input . mouseContext of
OverInvDrag k mpos _ _ ->
input . mouseContext .~ MouseInGame $
fromMaybe w $ tryDropSelected mpos w <|> tryPickupSelected k mpos w
OverInvDragSelect ssel mesel
| ScancodeLShift `M.member` (w ^. input . pressedKeys) ->
w & input . mouseContext .~ MouseInGame
& hud . hudElement . diSelection . _Just . _3
%~ getuniques
( maybe
mempty
(h ssel)
( guard (ssel ^? _1 == w ^? hud . hudElement . diSelection . _Just . _1)
>> mesel ^? _Just
)
)
OverInvDragSelect _ Nothing ->
w & input . mouseContext .~ MouseInGame
& hud . hudElement . diSelection . _Just . _3 %~ const mempty
OverInvDragSelect ssel (Just esel) ->
w & input . mouseContext .~ MouseInGame
& augInvDirectSelect (f (fst ssel, esel) (h ssel esel))
_ -> w
where
getuniques x y = IS.union x y IS.\\ IS.intersection x y
f (x, y) z = (x, y, z)
h (k, i) j = fold $ do
sss <- w ^? hud . hudElement . diSections . ix k . ssItems
let (_, xss) = IM.split (min i j -1) sss
(yss, _) = IM.split (max i j + 1) xss
return . IM.keysSet $ yss
isGroupSelectableSection :: Int -> Bool
isGroupSelectableSection = \case
0 -> True
3 -> True
_ -> False
updateMouseClickInGame :: Configuration -> World -> World
updateMouseClickInGame cfig w = case w ^. input . mouseContext of
MouseInGame -> fromMaybe w $ do
sss <- w ^? hud . hudElement . diSections
ysel <- inverseSelSecYint
(posSelSecYint cfig invDP (w ^. input . mousePos . _y)) sss
guard (isGroupSelectableSection $ fst ysel)
return $ w & input . mouseContext .~ OverInvDragSelect ysel Nothing
OverInvSelect (-1, _)
| selsec == Just (-1) ->
w
& hud . hudElement . diSelection %~ endRegex (-1) w
& hud . hudElement . diInvFilter .~ Nothing
OverInvSelect (2, _)
| selsec == Just 2 ->
w
& hud . hudElement . diSelection %~ endRegex 2 w
& hud . hudElement . diCloseFilter .~ Nothing
OverInvSelect x
| ScancodeLShift `M.member` (w ^. input . pressedKeys)
&& isGroupSelectableSection (fst x) ->
w & input . mouseContext .~ OverInvDragSelect x (Just $ snd x)
OverInvSelect x -> startDrag x cfig w
OverTerminalReturn tmid -> terminalReturnEffect tmid w
OverTerminalEscape -> w & hud . hudElement . subInventory .~ NoSubInventory
OverCombSelect x ->
w & hud . hudElement . subInventory . ciSelection ?~ f x
& worldEventFlags . at CombineInventoryChange ?~ ()
OverCombFilter ->
w & hud . hudElement . subInventory . ciFilter .~ Nothing
& worldEventFlags . at CombineInventoryChange ?~ ()
& hud . hudElement . subInventory . ciSelection %~ endCombineRegex w
OverCombCombine x ->
w
& tryCombine x
& worldEventFlags . at CombineInventoryChange ?~ ()
& worldEventFlags . at InventoryChange ?~ ()
& maybeExitCombine
OverCombEscape ->
w
& worldEventFlags . at InventoryChange ?~ ()
& hud . hudElement . subInventory .~ NoSubInventory
OverCombFiltInv (0, j) -> fromMaybe w $ do
str <-
fmap (take 5) $
w ^? cWorld . lWorld . creatures . ix 0 . crInv . ix j
>>= (listToMaybe . basicItemDisplay)
return . (worldEventFlags . at CombineInventoryChange ?~ ()) $
case w ^? hud . hudElement . subInventory . ciFilter . _Just of
Just ('#' : xs) | str == xs ->
w & hud . hudElement . subInventory . ciFilter .~ Nothing
_ -> w & hud . hudElement . subInventory . ciFilter ?~ ("#" ++ str)
_ -> w
where
f (x, y) = (x, y, mempty)
selsec = w ^? hud . hudElement . diSelection . _Just . _1
endRegex ::
Int ->
World ->
Maybe (Int, Int, IS.IntSet) ->
Maybe (Int, Int, IS.IntSet)
endRegex i w = fromMaybe id $ do
sss <- w ^? hud . hudElement . diSections
let j = fromMaybe 0 $ do
itms <- sss ^? ix i . ssItems
(k, _) <- IM.lookupMin itms
return k
return $ ssSetCursor (ssLookupDown i j) sss
endCombineRegex ::
World ->
Maybe (Int, Int, IS.IntSet) ->
Maybe (Int, Int, IS.IntSet)
endCombineRegex w = fromMaybe id $ do
sss <- w ^? hud . hudElement . subInventory . ciSections
let j = fromMaybe 0 $ do
itms <- sss ^? ix 0 . ssItems
(k, _) <- IM.lookupMin itms
return (k -1)
return $ ssSetCursor (ssLookupDown 0 j) sss
startDrag :: (Int, Int) -> Configuration -> World -> World
startDrag (a, b) cfig w = setcontext
$ case w ^? hud . hudElement . diSelection . _Just of
Just (i,_,xs) | i == a && b `IS.member` xs -> w
_ -> augInvDirectSelect (a, b, IS.singleton b) w
where
setcontext = input . mouseContext .~ OverInvDrag a (Just (a, b)) above bneath
above = inverseSelSecYint (yint - 1) =<< w ^? hud . hudElement . diSections
bneath = inverseSelSecYint (yint + 1) =<< w ^? hud . hudElement . diSections
yint = posSelSecYint cfig invDP (w ^. input . mousePos . _y)
concurrentIS :: IS.IntSet -> Bool
concurrentIS = go . IS.minView
where
go x = fromMaybe True $ do
(i,is) <- x
(j, js) <- IS.minView is
return $ i + 1 == j && go (Just (j, js))
collectInvItems :: Int -> IS.IntSet -> World -> World
collectInvItems secid is w = fromMaybe w $ do
guard $ secid == 0
(j, js) <- IS.minView is
return $ h j js w
where
h j js w' = fromMaybe w' $ do
(k, ks) <- IS.minView js
return . h (j + 1) ks $ swapInvItems (\_ _ -> Just (j + 1)) k w'
shiftInvItems ::
Int -> -- recurse limit
Int -> -- section where drag started
(Int, Int) -> -- selection item mouse is over
Maybe (Int, Int) -> -- selection item "above" mouse position (can be same as over)
Maybe (Int, Int) -> -- selection item "below" mouse position (can be same as over)
IS.IntSet ->
IM.IntMap (SelectionItem a) ->
World ->
World
shiftInvItems n k x ab bn xs ss w = setSelWhileDragging . fromMaybe w $ do
let xk = fst x
guard $ xk == k || xk + 1 == k || xk -1 == k
(maxi, _) <- IS.maxView xs
(mini, _) <- IS.minView xs
case True of
_ | x < (k, mini) -> do
guard $ not . null . fst $ IM.split mini ss
guard $ Just (k, mini -1) /= ab
return . doDrag (n -1) k (Just x) ab bn $ shiftInvItemsUp k xs w
_ | x > (k, maxi) -> do
guard $ not . null . snd $ IM.split maxi ss
guard $ Just (k, maxi + 1) /= bn
return . doDrag (n -1) k (Just x) ab bn $ shiftInvItemsDown k xs w
_ -> Nothing
setSelWhileDragging :: World -> World
setSelWhileDragging w = fromMaybe w $ do
(i, _, xs) <- w ^? hud . hudElement . diSelection . _Just
(k, j) <- w ^? input . mouseContext . mcoMaybeSelect . _Just
guard $ i == k && j `IS.member` xs
return $ w & hud . hudElement . diSelection . _Just . _2 .~ j
shiftInvItemsUp :: Int -> IS.IntSet -> World -> World
shiftInvItemsUp j is w = IS.foldl' f w is
where
f w' i' = swapItemWith g (j, i') w'
g i' m = fst <$> IM.lookupLT i' m
shiftInvItemsDown :: Int -> IS.IntSet -> World -> World
shiftInvItemsDown j is w = fromMaybe w $ do
let i = IS.findMax is
guard . isJust $
w ^? hud . hudElement . diSections . ix j . ssItems . ix i
return $ IS.foldr f w is
where
f i' w' = swapItemWith g (j, i') w'
g i' m = fst <$> IM.lookupGT i' m
updateFunctionKeys :: Universe -> Universe
updateFunctionKeys u =
M.foldlWithKey'
updateFunctionKey
u
(u ^. uvWorld . input . pressedKeys)
updateFunctionKey :: Universe -> Scancode -> PressType -> Universe
updateFunctionKey uv sc InitialPress = case sc of
ScancodeF1 -> useNormalCamera uv
ScancodeF2 -> pauseAndFloatCam uv
ScancodeF5 -> doQuicksave uv
ScancodeF9 -> doQuickload uv
ScancodeF6 -> doDebugTestF6 uv
ScancodeF7 -> doDebugTestF7 uv
ScancodeF8 -> doDebugTestF8 uv
ScancodeF10 -> doDebugTestF10 uv
ScancodeF11 -> doDebugTestF11 uv
ScancodeF12 -> doDebugTestF12 uv
ScancodeEscape -> pauseGame uv
_ -> uv
updateFunctionKey uv ScancodeF3 _ = doDebugTest uv
updateFunctionKey uv ScancodeF4 _ = doDebugTest2 uv
updateFunctionKey uv _ _ = uv
updateKeysInTerminal :: Int -> Universe -> Universe
updateKeysInTerminal tmid u =
u
& doTextInputOverUniverse
(uvWorld . cWorld . lWorld . terminals . ix tmid . tmInput . tiText)
& checkEndStatus
where
checkEndStatus
| u ^. uvWorld . input . pressedKeys . at ScancodeReturn == Just InitialPress =
uvWorld %~ terminalReturnEffect tmid
| otherwise = id
updateKeyInGame :: Universe -> Scancode -> PressType -> Universe
updateKeyInGame uv sc pt = case pt of
InitialPress -> updateInitialPressInGame uv sc
LongPress -> updateLongPressInGame uv sc
_ -> uv
updateInitialPressInGame :: Universe -> Scancode -> Universe
updateInitialPressInGame uv sc = case sc of
ScancodeSpace -> over uvWorld spaceAction uv
ScancodeP -> pauseGame uv
ScancodeF -> over uvWorld youDropItem uv
-- ScancodeM -> toggleMap uv
ScancodeT -> over uvWorld testEvent uv
ScancodeC -> toggleCombineInv uv
-- the following should be put in a more sensible place
ScancodeSlash -> uv & uvWorld %~ updateEnterRegex
ScancodeBackspace -> uv & uvWorld %~ updateBackspaceRegex
_ -> uv
updateLongPressInGame :: Universe -> Scancode -> Universe
updateLongPressInGame uv sc = case sc of
ScancodeF -> over uvWorld youDropItem uv
ScancodeSpace -> over uvWorld spaceAction uv
_ -> uv
doRegexInput ::
Input ->
Int ->
IM.IntMap (SelectionSection a) ->
Maybe (Int, Int, IS.IntSet) ->
Maybe String ->
(IM.IntMap (SelectionSection a), Maybe (Int, Int, IS.IntSet), Maybe String)
doRegexInput inp i sss msel filts
| backspacetonothing || escapekey = endregex i 0
| endkeys = endregex (i + 1) (j -1)
| otherwise =
( sss
, msel
, filts & doTextInputOver inp _Just
)
where
endregex a b =
( sss
, msel & ssSetCursor (ssLookupDown a b) sss
, Nothing
)
j = fromMaybe 0 $ do
itms <- sss ^? ix (i + 1) . ssItems
fst <$> IM.lookupMin itms
escapekey = ScancodeEscape `M.lookup` pkeys == Just InitialPress
endkeys =
any
((== Just InitialPress) . (`M.lookup` pkeys))
[ScancodeReturn, ScancodeSlash]
backspacetonothing =
filts == Just ""
&& ScancodeBackspace `M.lookup` pkeys == Just InitialPress
pkeys = inp ^. pressedKeys
updateBackspaceRegex :: World -> World
updateBackspaceRegex w = case di ^? subInventory of
Just NoSubInventory{}
| secfocus (-1) 0 ->
w & hud . hudElement %~ trybackspace (-1) diInvFilter diInvFilter diSelection
& worldEventFlags . at InventoryChange ?~ ()
Just NoSubInventory{}
| secfocus 2 3 ->
w & hud . hudElement %~ trybackspace 2 diCloseFilter diCloseFilter diSelection
& worldEventFlags . at InventoryChange ?~ ()
Just CombineInventory{} ->
w & hud . hudElement . subInventory %~ trybackspace (-1) ciFilter ciFilter ciSelection
& worldEventFlags . at InventoryChange ?~ ()
_ -> w
where
secfocus a b = fromMaybe False $ do
i <- di ^? diSelection . _Just . _1
return $ i == a || i == b
trybackspace x filtget filtset selset he = fromMaybe he $ do
str <- he ^? filtget . _Just
return $ case str of
(_ : _) ->
he & filtset . _Just %~ init
& selset ?~ (x, 0, mempty)
[] -> he & filtset .~ Nothing
di = w ^. hud . hudElement
updateEnterRegex :: World -> World
updateEnterRegex w = case w ^? hud . hudElement . subInventory of
Just NoSubInventory{}
| secfocus [-1, 0, 1] ->
w & hud . hudElement . diSelection ?~ (-1, 0, mempty)
& hud . hudElement . diInvFilter %~ enterregex
Just NoSubInventory{}
| secfocus [2, 3] ->
w & hud . hudElement . diSelection ?~ (2, 0, mempty)
& hud . hudElement . diCloseFilter %~ enterregex
Just CombineInventory{} ->
w & hud . hudElement . subInventory . ciFilter %~ enterregex
& hud . hudElement . subInventory . ciSelection ?~ (-1, 0, mempty)
_ -> w
where
di = w ^. hud . hudElement
secfocus xs = fromMaybe False $ do
i <- di ^? diSelection . _Just . _1
return $ i `elem` xs
enterregex = (<|> Just "")
pauseGame :: Universe -> Universe
pauseGame u = u & uvScreenLayers .~ [pauseMenu u]
spaceAction :: World -> World
spaceAction w = case w ^. hud . hudElement of
DisplayInventory{_subInventory = NoSubInventory{}} ->
maybe id interactWithCloseObj (getCloseObj w) w
DisplayInventory{_subInventory = DisplayTerminal{}} ->
w
& hud . hudElement . subInventory .~ NoSubInventory
& worldEventFlags . at InventoryChange ?~ ()
_ -> w & hud . hudElement . subInventory .~ NoSubInventory
getCloseObj :: World -> Maybe (Either FloorItem Button)
getCloseObj w = getSelectedCloseObj w <|> firstcitem <|> firstcbut
where
firstcitem = do
k' <- (w ^? hud . hudElement . diSections . ix 3 . ssItems) >>= (fmap fst . IM.lookupMin)
NInt k <- w ^? hud . closeItems . ix k'
fmap Left $ w ^? cWorld . lWorld . floorItems . unNIntMap . ix k
firstcbut = do
k <- w ^? hud . closeButtons . ix 0
fmap Right $ w ^? cWorld . lWorld . buttons . ix k
tryCombine :: (Int, Int) -> World -> World
tryCombine (i, j) w = fromMaybe w $ do
sss <- w ^? hud . hudElement . subInventory . ciSections
CombinableItem is it <- sss ^? ix i . ssItems . ix j . siPayload
p <- w ^? cWorld . lWorld . creatures . ix 0 . crPos
return $
snd (createItemYou it (foldr (destroyInvItem 0) w (sort is)))
& soundStart InventorySound p wrench1S Nothing
& hud . hudElement . diSelection . _Just . _3 .~ mempty
maybeExitCombine :: World -> World
maybeExitCombine w
| ButtonRight `M.member` (w ^. input . mouseButtons) = w
| otherwise =
w & hud . hudElement . subInventory .~ NoSubInventory