Permalink
Browse files

multiple heroes: monster attacks any hero correctly; infrastructure c…

…omplete

Plus a bit of clean-up.
  • Loading branch information...
1 parent 4b4d2cb commit adc3f4f84ce14c8086e557a8acfa0adf4fa1c945 @Mikolaj Mikolaj committed Jan 30, 2011
Showing with 54 additions and 35 deletions.
  1. +38 −24 src/Actions.hs
  2. +6 −6 src/Dungeon.hs
  3. +2 −2 src/Perception.hs
  4. +7 −2 src/State.hs
  5. +1 −1 src/StrategyState.hs
View
@@ -288,37 +288,46 @@ fleeDungeon =
-- TODO: extend to number keys switching to heroes on any levels.
cycleHero =
do
- state <- get
- let player = splayer state
- i = playerNumber player
- pls = lplayers (slevel state)
- case slook state of
+ pls <- gets (lplayers . slevel)
+ nln <- gets (lname . slevel)
+ player <- gets splayer
+ look <- gets slook
+ case look of
Just (_loc, _tgt, ln)
- | ln /= lname (slevel state) ->
+ | ln /= nln ->
case IM.assocs pls of
- [] -> abortWith "No heroes on this level."
+ [] -> abortWith "No heroes on this level."
(ni, np) : _ ->
do
- let ins = IM.insert i player
+ let i = playerNumber player
+ ins = IM.insert i player
pli = updatePlayers ins
del = IM.delete ni
- nln = lname (slevel state)
modify (updateDungeon (updateDungeonLevel pli ln))
modify (updateLevel (updatePlayers del))
modify (updatePlayer (const np))
modify (updateLook (const $ Just (_loc, _tgt, nln)))
messageAdd "A hero selected."
_ ->
- let (lt, gt) = IM.split i pls
+ let i = playerNumber player
+ (lt, gt) = IM.split i pls
in case IM.assocs gt ++ IM.assocs lt of
- [] -> abortWith "Only one hero on this level."
+ [] -> abortWith "Only one hero on this level."
(ni, np) : _ ->
do
- let upd pls = IM.insert i player $ IM.delete ni pls
- modify (updateLevel (updatePlayers upd))
- modify (updatePlayer (const np))
+ swapCurrentHero (ni, np)
messageAdd "Next hero selected."
+swapCurrentHero :: (Int, Player) -> Action ()
+swapCurrentHero (ni, np) =
+ do
+ player <- gets splayer
+ let i = playerNumber player
+ upd pls = IM.insert i player $ IM.delete ni pls
+ when (ni == i) abort
+ modify (updateLevel (updatePlayers upd))
+ modify (updatePlayer (const np))
+
-- | Calculate loot's worth. TODO: move to another module, and refine significantly. TODO: calculate for all players on the current level.
calculateTotal :: Player -> Int
calculateTotal player = L.sum $ L.map price $ mitems player
@@ -606,18 +615,23 @@ moveOrAttack allowAttacks autoOpen actor dir
do
-- We start by looking at the target position.
state <- get
- let lvl@(Level { lmap = lmap }) = slevel state
- let player = splayer state
+ monsters <- gets (lmonsters . slevel)
+ lmap <- gets (lmap . slevel)
let monster = getActor state actor
- let loc = mloc monster -- current location
- let s = lmap `at` loc -- tile at current location
- let nloc = loc `shift` dir -- target location
- let t = lmap `at` nloc -- tile at target location
- let attackedPlayer = [ APlayer | mloc player == nloc ]
- let attackedMonsters = L.map AMonster $
- findIndices (\ m -> mloc m == nloc) (lmonsters lvl)
- let attacked :: [Actor]
+ loc = mloc monster -- current location
+ s = lmap `at` loc -- tile at current location
+ nloc = loc `shift` dir -- target location
+ t = lmap `at` nloc -- tile at target location
+ ps = levelHeroAssocs state
+ attPlayer = find (\ (_, m) -> mloc m == nloc) ps
+ attackedPlayer = if isJust attPlayer then [APlayer] else []
+ attMonsters = findIndices (\ m -> mloc m == nloc) monsters
+ attackedMonsters = L.map AMonster $ attMonsters
+ attacked :: [Actor]
attacked = attackedPlayer ++ attackedMonsters
+ -- Focus on the attacked hero, if any.
+ -- TODO: This requires a special case if a hero bumps into another.
+ maybe (return ()) swapCurrentHero attPlayer
-- At the moment, we check whether there is a monster before checking accessibility
-- i.e., we can attack a monster on a blocked location. For instance,
-- a monster on an open door can be attacked diagonally, and a
View
@@ -313,16 +313,16 @@ addMonster state@(State { slevel = lvl@(Level { lmonsters = ms,
if rc
then
do
- let ps = levelPlayerList state
+ let hs = levelHeroList state
-- TODO: new monsters should always be generated in a place that isn't
-- visible by the player (if possible -- not possible for bigrooms)
-- levels with few rooms are dangerous, because monsters may spawn
-- in adjacent and unexpected places
sm <- findLocTry 1000 lvl
(\ l t -> open t
- && not (l `L.elem` L.map mloc (ps ++ ms)))
+ && not (l `L.elem` L.map mloc (hs ++ ms)))
(\ l t -> floor t
- && L.all (\pl -> distance (mloc pl, l) > 400) ps)
+ && L.all (\pl -> distance (mloc pl, l) > 400) hs)
m <- newMonster sm monsterFrequency
return (updateMonsters (const (m : ms)) lvl)
else return lvl
@@ -332,11 +332,11 @@ addHero :: Int -> State -> Int -> Rnd State
addHero hp state@(State { splayer = player,
slevel = lvl@(Level { lmonsters = ms }) }) n =
do
- let ps = levelPlayerList state
+ let hs = levelHeroList state
ploc <- findLocTry 10000 lvl -- TODO: bad for large levels
(\ l t -> open t
- && not (l `L.elem` L.map mloc (ps ++ ms)))
+ && not (l `L.elem` L.map mloc (hs ++ ms)))
(\ l t -> floor t
- && distance (mloc player, l) < 5 + L.length ps `div` 3)
+ && distance (mloc player, l) < 5 + L.length hs `div` 3)
let hero = defaultPlayer n ploc hp
return (updateLevel (updatePlayers (IM.insert n hero)) state)
View
@@ -34,8 +34,8 @@ perception_ state@(State { slevel = Level { lmap = lmap },
_ -> error $ "perception_: unknown mode: " ++ show mode
- ps = levelPlayerList state
- pers = L.map (\ pl -> perception fovMode (mloc pl) lmap) ps
+ hs = levelHeroList state
+ pers = L.map (\ pl -> perception fovMode (mloc pl) lmap) hs
reachable = S.unions (L.map preachable pers)
visible = S.unions (L.map pvisible pers)
in Perception reachable visible
View
@@ -50,11 +50,16 @@ defaultState player dng lvl =
updatePlayer :: (Monster -> Monster) -> State -> State
updatePlayer f s = s { splayer = f (splayer s) }
-levelPlayerList :: State -> [Player]
-levelPlayerList (State { splayer = player,
+levelHeroList :: State -> [Player]
+levelHeroList (State { splayer = player,
slevel = Level { lplayers = pls } }) =
player : IM.elems pls
+levelHeroAssocs :: State -> [(Int, Player)]
+levelHeroAssocs (State { splayer = player,
+ slevel = Level { lplayers = pls } }) =
+ (playerNumber player, player) : IM.assocs pls
+
updateLook ::
(Maybe (Loc, Target, LevelName) -> Maybe (Loc, Target, LevelName)) ->
State -> State
View
@@ -27,7 +27,7 @@ strategy m@(Monster { mtype = mt, mloc = me, mdir = mdir })
-- player is visible by the monster -- this is more efficient, but
-- is not correct with the Shadow FOV (the other FOVs are symmetrical)
-- TODO: set monster targets and then prefer targets to other heroes
- plocs = L.map mloc (levelPlayerList state)
+ plocs = L.map mloc (levelHeroList state)
plds = L.map (\ l -> (distance (me, l), l)) plocs
-- we have to sort the list to avoid bias towards the currently selected
-- hero; instead monsters will prefer heroes with smaller locations

0 comments on commit adc3f4f

Please sign in to comment.