about summary refs log tree commit diff
path: root/src/Xanthous/AI/Gormlak.hs
blob: 268e33ad6caacab29de1cad11380d9b5d9a6eb7a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
{-# OPTIONS_GHC -fno-warn-orphans #-}
--------------------------------------------------------------------------------
module Xanthous.AI.Gormlak () where
--------------------------------------------------------------------------------
import           Xanthous.Prelude hiding (lines)
--------------------------------------------------------------------------------
import           Data.Coerce
import           Control.Monad.State
import           Control.Monad.Random
import           Data.Aeson (object)
import qualified Data.Aeson as A
--------------------------------------------------------------------------------
import           Xanthous.Data
                 ( Positioned(..), positioned, position
                 , diffPositions, stepTowards, isUnit
                 , Ticks, (|*|), invertedRate
                 )
import           Xanthous.Data.EntityMap
import qualified Xanthous.Entities.Creature as Creature
import           Xanthous.Entities.Creature
                 ( Creature, hippocampus, creatureType
                 , destination, destinationProgress, destinationPosition
                 )
import           Xanthous.Entities.Character (Character)
import qualified Xanthous.Entities.Character as Character
import qualified Xanthous.Entities.RawTypes as Raw
import           Xanthous.Entities (Entity(..), Brain(..), brainVia)
import           Xanthous.Game.State (entities, GameState, entityIs)
import           Xanthous.Game.Lenses
                 ( Collision(..), entityCollision, collisionAt
                 , character, characterPosition
                 )
import           Xanthous.Data.EntityMap.Graphics (linesOfSight, canSee)
import           Xanthous.Random
import           Xanthous.Monad (say)
--------------------------------------------------------------------------------

stepGormlak
  :: (MonadState GameState m, MonadRandom m)
  => Ticks
  -> Positioned Creature
  -> m (Positioned Creature)
stepGormlak ticks pe@(Positioned pos creature) = do
  dest <- maybe (selectDestination pos creature) pure
         $ creature ^. hippocampus . destination
  let progress' =
        dest ^. destinationProgress
        + creature ^. creatureType . Raw.speed . invertedRate |*| ticks
  if progress' < 1
    then pure
         $ pe
         & positioned . hippocampus . destination
         ?~ (dest & destinationProgress .~ progress')
    else do
      let newPos = dest ^. destinationPosition
          remainingSpeed = progress' - 1
      newDest <- selectDestination newPos creature
                <&> destinationProgress +~ remainingSpeed
      let pe' = pe & positioned . hippocampus . destination ?~ newDest
      collisionAt newPos >>= \case
        Nothing -> pure $ pe' & position .~ newPos
        Just Stop -> pure pe'
        Just Combat -> do
          ents <- use $ entities . atPosition newPos
          when (any (entityIs @Character) ents) attackCharacter
          pure pe'
  where
    selectDestination pos' creature' = Creature.destinationFromPos <$> do
      canSeeCharacter <- uses entities $ canSee (entityIs @Character) pos' vision
      if canSeeCharacter
        then do
          charPos <- use characterPosition
          if isUnit (pos' `diffPositions` charPos)
            then attackCharacter $> pos'
            else pure $ pos' `stepTowards` charPos
      else do
        lines <- map (takeWhile (isNothing . entityCollision . map snd . snd)
                    -- the first item on these lines is always the creature itself
                    . fromMaybe mempty . tailMay)
                . linesOfSight pos' (Creature.visionRadius creature')
                <$> use entities
        line <- choose $ weightedBy length lines
        pure $ fromMaybe pos' $ fmap fst . headMay =<< line

    vision = Creature.visionRadius creature
    attackCharacter = do
      say ["combat", "creatureAttack"] $ object [ "creature" A..= creature ]
      character %= Character.damage 1

newtype GormlakBrain = GormlakBrain Creature

instance Brain GormlakBrain where
  step ticks = fmap coerce . stepGormlak ticks . coerce

--------------------------------------------------------------------------------

instance Brain Creature where step = brainVia GormlakBrain

instance Entity Creature where
  blocksVision _ = False
  description = view $ Creature.creatureType . Raw.description