feat(xanthous): Allow selecting hand for wielding

When wielding items, allow selecting which hand the item should be
wielded in.

Currently this has no actual effect on the mechanics of combat - that'll
come next.

Change-Id: Ic289ca2d8fa6f5fc0ad5bd0b012818a3acd8599e
Reviewed-on: https://cl.tvl.fyi/c/depot/+/5470
Reviewed-by: grfn <grfn@gws.fyi>
Autosubmit: grfn <grfn@gws.fyi>
Tested-by: BuildkiteCI
This commit is contained in:
Griffin Smith 2022-04-16 16:01:04 -04:00 committed by clbot
parent 8da2fce9ef
commit 632c4280b5
4 changed files with 119 additions and 30 deletions

View file

@ -54,12 +54,12 @@ import Xanthous.Physics (throwDistance, bluntThrowDamage)
import Xanthous.Data.EntityMap.Graphics (lineOfSight) import Xanthous.Data.EntityMap.Graphics (lineOfSight)
import Xanthous.Data.EntityMap (EntityID) import Xanthous.Data.EntityMap (EntityID)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
--------------------------------------------------------------------------------
import Xanthous.Entities.Common import Xanthous.Entities.Common
( InventoryPosition, describeInventoryPosition, backpack ( InventoryPosition, describeInventoryPosition, backpack
, wieldableItem, wieldedItems, wielded, itemsWithPosition , wieldableItem, wieldedItems, wielded, itemsWithPosition
, removeItemFromPosition, asWieldedItem, inRightHand , removeItemFromPosition, asWieldedItem
, wieldedItem, items , wieldedItem, items, Hand (..), describeHand, wieldInHand
, WieldedItem
) )
import qualified Xanthous.Entities.Character as Character import qualified Xanthous.Entities.Character as Character
import Xanthous.Entities.Character hiding (pickUpItem) import Xanthous.Entities.Character hiding (pickUpItem)
@ -296,14 +296,30 @@ handleCommand DescribeInventory = do
handleCommand Wield = do handleCommand Wield = do
takeItemFromInventory_ ["wield", "menu"] Cancellable asWieldedItem selectItem $ \(MenuResult (item :: WieldedItem)) -> do
(say_ ["wield", "nothing"]) selectHand $ \(MenuResult hand) -> do
$ \(MenuResult item) -> do prevItems <- character . inventory . wielded %%= wieldInHand hand item
prevItems <- character . inventory . wielded <<.= inRightHand item
character . inventory . backpack character . inventory . backpack
<>= fromList (prevItems ^.. wieldedItems . wieldedItem) <>= fromList (map (view wieldedItem) prevItems)
say ["wield", "wielded"] item say ["wield", "wielded"] $ object [ "item" A..= item
, "hand" A..= describeHand hand
]
continue continue
where
selectItem =
takeItemFromInventory_ ["wield", "menu"] Cancellable asWieldedItem
(say_ ["wield", "nothing"])
selectHand
= menu_
["wield", "hand"]
Cancellable
handsMenu
handsMenu = mapFromList
. map (second $ MenuOption =<< describeHand)
$ [ ('l', LeftHand)
, ('r', RightHand)
, ('b', BothHands)
]
handleCommand Fire = do handleCommand Fire = do
selectItemFromInventory_ ["fire", "menu"] Cancellable id selectItemFromInventory_ ["fire", "menu"] Cancellable id

View file

@ -20,12 +20,18 @@ module Xanthous.Entities.Common
-- ** Wielded items -- ** Wielded items
, Wielded(..) , Wielded(..)
, nothingWielded
, hands , hands
, leftHand , leftHand
, rightHand , rightHand
, inLeftHand , inLeftHand
, inRightHand , inRightHand
, doubleHanded , doubleHanded
, Hand(..)
, itemsInHand
, inHand
, wieldInHand
, describeHand
, wieldedItems , wieldedItems
, WieldedItem(..) , WieldedItem(..)
, wieldedItem , wieldedItem
@ -95,6 +101,7 @@ data Wielded
via WithOptions '[ 'SumEnc 'ObjWithSingleField ] via WithOptions '[ 'SumEnc 'ObjWithSingleField ]
Wielded Wielded
nothingWielded :: Wielded nothingWielded :: Wielded
nothingWielded = Hands Nothing Nothing nothingWielded = Hands Nothing Nothing
@ -124,6 +131,43 @@ wieldedItems :: Traversal' Wielded WieldedItem
wieldedItems k (DoubleHanded wielded) = DoubleHanded <$> k wielded wieldedItems k (DoubleHanded wielded) = DoubleHanded <$> k wielded
wieldedItems k (Hands l r) = Hands <$> _Just k l <*> _Just k r wieldedItems k (Hands l r) = Hands <$> _Just k l <*> _Just k r
data Hand
= LeftHand
| RightHand
| BothHands
deriving stock (Eq, Show, Ord, Generic)
deriving anyclass (NFData, CoArbitrary, Function)
deriving Arbitrary via GenericArbitrary Hand
itemsInHand :: Hand -> Wielded -> [WieldedItem]
itemsInHand LeftHand (DoubleHanded wi) = [wi]
itemsInHand LeftHand (Hands lh _) = toList lh
itemsInHand RightHand (DoubleHanded wi) = [wi]
itemsInHand RightHand (Hands _ rh) = toList rh
itemsInHand BothHands (DoubleHanded wi) = [wi]
itemsInHand BothHands (Hands lh rh) = toList lh <> toList rh
inHand :: Hand -> WieldedItem -> Wielded
inHand LeftHand = inLeftHand
inHand RightHand = inRightHand
inHand BothHands = review doubleHanded
wieldInHand :: Hand -> WieldedItem -> Wielded -> ([WieldedItem], Wielded)
wieldInHand hand item w = (itemsInHand hand w, doWield)
where
doWield = case (hand, w) of
(LeftHand, Hands _ r) -> Hands (Just item) r
(LeftHand, DoubleHanded _) -> inLeftHand item
(RightHand, Hands l _) -> Hands l (Just item)
(RightHand, DoubleHanded _) -> inRightHand item
(BothHands, _) -> DoubleHanded item
describeHand :: Hand -> Text
describeHand LeftHand = "your left hand"
describeHand RightHand = "your right hand"
describeHand BothHands = "both hands"
data Inventory = Inventory data Inventory = Inventory
{ _backpack :: Vector Item { _backpack :: Vector Item
, _wielded :: Wielded , _wielded :: Wielded
@ -199,27 +243,23 @@ class HasInventory s a | s -> a where
-- | Representation for where in the inventory an item might be -- | Representation for where in the inventory an item might be
data InventoryPosition data InventoryPosition
= Backpack = Backpack
| LeftHand | InHand Hand
| RightHand
| BothHands
deriving stock (Eq, Show, Ord, Generic) deriving stock (Eq, Show, Ord, Generic)
deriving anyclass (NFData, CoArbitrary, Function) deriving anyclass (NFData, CoArbitrary, Function)
deriving Arbitrary via GenericArbitrary InventoryPosition deriving Arbitrary via GenericArbitrary InventoryPosition
-- | Return a human-readable description of the given 'InventoryPosition' -- | Return a human-readable description of the given 'InventoryPosition'
describeInventoryPosition :: InventoryPosition -> Text describeInventoryPosition :: InventoryPosition -> Text
describeInventoryPosition Backpack = "In backpack" describeInventoryPosition Backpack = "In backpack"
describeInventoryPosition LeftHand = "Wielded, in left hand" describeInventoryPosition (InHand hand) = "Wielded, in " <> describeHand hand
describeInventoryPosition RightHand = "Wielded, in right hand"
describeInventoryPosition BothHands = "Wielded, in both hands"
-- | Given a position in the inventory, return a traversal on the inventory over -- | Given a position in the inventory, return a traversal on the inventory over
-- all the items in that position -- all the items in that position
inventoryPosition :: InventoryPosition -> Traversal' Inventory Item inventoryPosition :: InventoryPosition -> Traversal' Inventory Item
inventoryPosition Backpack = backpack . traversed inventoryPosition Backpack = backpack . traversed
inventoryPosition LeftHand = wielded . leftHand . _Just . wieldedItem inventoryPosition (InHand LeftHand) = wielded . leftHand . _Just . wieldedItem
inventoryPosition RightHand = wielded . leftHand . _Just . wieldedItem inventoryPosition (InHand RightHand) = wielded . leftHand . _Just . wieldedItem
inventoryPosition BothHands = wielded . doubleHanded . wieldedItem inventoryPosition (InHand BothHands) = wielded . doubleHanded . wieldedItem
-- | A fold over all the items in the inventory accompanied by their position in -- | A fold over all the items in the inventory accompanied by their position in
-- the inventory -- the inventory
@ -230,20 +270,20 @@ itemsWithPosition = folding $ (<>) <$> backpackItems <*> handItems
where where
backpackItems = toListOf $ backpack . folded . to (Backpack ,) backpackItems = toListOf $ backpack . folded . to (Backpack ,)
handItems inv = case inv ^. wielded of handItems inv = case inv ^. wielded of
DoubleHanded i -> pure (BothHands, i ^. wieldedItem) DoubleHanded i -> pure (InHand BothHands, i ^. wieldedItem)
Hands l r -> (l ^.. folded . wieldedItem . to (LeftHand ,)) Hands l r -> (l ^.. folded . wieldedItem . to (InHand LeftHand ,))
<> (r ^.. folded . wieldedItem . to (RightHand ,)) <> (r ^.. folded . wieldedItem . to (InHand RightHand ,))
-- | Remove the first item equal to 'Item' from the given position in the -- | Remove the first item equal to 'Item' from the given position in the
-- inventory -- inventory
removeItemFromPosition :: InventoryPosition -> Item -> Inventory -> Inventory removeItemFromPosition :: InventoryPosition -> Item -> Inventory -> Inventory
removeItemFromPosition Backpack item inv removeItemFromPosition Backpack item inv
= inv & backpack %~ removeFirst (== item) = inv & backpack %~ removeFirst (== item)
removeItemFromPosition LeftHand item inv removeItemFromPosition (InHand LeftHand) item inv
= inv & wielded . leftHand %~ filter ((/= item) . view wieldedItem) = inv & wielded . leftHand %~ filter ((/= item) . view wieldedItem)
removeItemFromPosition RightHand item inv removeItemFromPosition (InHand RightHand) item inv
= inv & wielded . rightHand %~ filter ((/= item) . view wieldedItem) = inv & wielded . rightHand %~ filter ((/= item) . view wieldedItem)
removeItemFromPosition BothHands item inv removeItemFromPosition (InHand BothHands) item inv
| has (wielded . doubleHanded . wieldedItem . filtered (== item)) inv | has (wielded . doubleHanded . wieldedItem . filtered (== item)) inv
= inv & wielded .~ nothingWielded = inv & wielded .~ nothingWielded
| otherwise | otherwise

View file

@ -115,8 +115,8 @@ wield:
- You can't wield anything in your backpack - You can't wield anything in your backpack
- You can't wield anything currently in your backpack - You can't wield anything currently in your backpack
menu: What would you like to wield? menu: What would you like to wield?
# TODO: use actual hands hand: Wield in which hand?
wielded : You wield the {{wieldedItem.itemType.name}} in your right hand. wielded: You wield the {{item.wieldedItem.itemType.name}} in {{hand}}
fire: fire:
nothing: nothing:

View file

@ -10,6 +10,17 @@ import Xanthous.Entities.Common
main :: IO () main :: IO ()
main = defaultMain test main = defaultMain test
newtype OneHand = OneHand Hand
deriving stock Show
instance Arbitrary OneHand where
arbitrary = OneHand <$> elements [LeftHand, RightHand]
otherHand :: Hand -> Hand
otherHand LeftHand = RightHand
otherHand RightHand = LeftHand
otherHand BothHands = error "OtherHand BothHands"
test :: TestTree test :: TestTree
test = testGroup "Xanthous.Entities.CommonSpec" test = testGroup "Xanthous.Entities.CommonSpec"
[ testGroup "Inventory" [ testGroup "Inventory"
@ -20,13 +31,35 @@ test = testGroup "Xanthous.Entities.CommonSpec"
let (old, inv') = inv & wielded <<.~ w let (old, inv') = inv & wielded <<.~ w
in inv' & backpack <>~ toVectorOf (wieldedItems . wieldedItem) old in inv' & backpack <>~ toVectorOf (wieldedItems . wieldedItem) old
in [ (Backpack, \item -> backpack %~ (item ^. wieldedItem <|)) in [ (Backpack, \item -> backpack %~ (item ^. wieldedItem <|))
, (LeftHand, rewield . inLeftHand) , (InHand LeftHand, rewield . inLeftHand)
, (RightHand, rewield . inRightHand) , (InHand RightHand, rewield . inRightHand)
, (BothHands, rewield . review doubleHanded) , (InHand BothHands, rewield . review doubleHanded)
] <&> \(pos, addItem) -> ] <&> \(pos, addItem) ->
testProperty (show pos) $ \inv item -> testProperty (show pos) $ \inv item ->
let inv' = addItem item inv let inv' = addItem item inv
inv'' = removeItemFromPosition pos (item ^. wieldedItem) inv' inv'' = removeItemFromPosition pos (item ^. wieldedItem) inv'
in inv'' ^.. items === inv ^.. items in inv'' ^.. items === inv ^.. items
] ]
, testGroup "Wielded items"
[ testGroup "wieldInHand"
[ testProperty "puts the item in the hand" $ \w hand item ->
let (_, w') = wieldInHand hand item w
in itemsInHand hand w' === [item]
, testProperty "returns items in both hands when wielding double-handed"
$ \lh rh newItem ->
let w = Hands (Just lh) (Just rh)
(prevItems, _) = wieldInHand BothHands newItem w
in prevItems === [lh, rh]
, testProperty "wielding in one hand leaves the item in the other hand"
$ \(OneHand h) existingItem newItem ->
let (_, w) = wieldInHand h existingItem nothingWielded
(prevItems, w') = wieldInHand (otherHand h) newItem w
in prevItems === []
.&&. sort (w' ^.. wieldedItems) === sort [existingItem, newItem]
, testProperty "always leaves the same items overall" $ \w hand item ->
let (prevItems, w') = wieldInHand hand item w
in sort (prevItems <> (w' ^.. wieldedItems))
=== sort (item : w ^.. wieldedItems)
]
]
] ]