Add various capabilities for generating graphs, fixes to lasgun

This commit is contained in:
2022-07-06 11:54:29 +01:00
parent cd3463d3d8
commit d63495e335
29 changed files with 1801 additions and 178 deletions
+203
View File
@@ -0,0 +1,203 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
-- | deals with placement of objects within the world
-- after they have had their coordinates set by the layout
module Dodge.Placement.PlaceSpot
( placeSpot
) where
import Dodge.Placement.Shift
import Dodge.Data
import Dodge.Path
import Dodge.Placement.PlaceSpot.Block
import Dodge.Placement.PlaceSpot.TriggerDoor
import Dodge.Default.Wall
import Dodge.ShiftPoint
import Dodge.Base.NewID
import Geometry
import qualified IntMapHelp as IM
import Color
import Data.Foldable
import Data.Maybe
import System.Random
import Control.Monad.State
import Control.Lens
import qualified Data.IntSet as IS
import Data.Bifunctor
-- when placing a placement, we update the world and the room and assign an id
-- to the placement
placeSpot :: (World,Room) -> Placement -> ( (World,Room), [Placement] )
placeSpot (w,rm) plmnt = case plmnt of
Placement{_plSpot = PSRoomRand i f} -> placeSpotRoomRand rm i f plmnt w
Placement{_plSpot = PSPos extract eff fallback} -> placeSpotUsingLink w rm plmnt extract eff fallback
Placement{} -> placePlainPSSpot w rm plmnt shift
PlacementUsingPos p subpl -> placeSpot (w,rm) (subpl (shiftPoint3By shift p))
RandomPlacement rplmnt -> placeRandomPlacement rplmnt w rm
PickOnePlacement i pl -> ((placePickOne i pl rm w, rm), [])
where
shift = _rmShift rm
placePickOne :: Int -> Placement -> Room -> World -> World
placePickOne i pl rm w = w & genPlacements %~ IM.insertWith (++) i [(pl, fromJust (_rmMID rm))]
placeRandomPlacement :: State StdGen Placement -> World -> Room -> ((World,Room),[Placement])
placeRandomPlacement rplmnt w rm = placeSpot (w & randGen .~ g,rm) plmnt'
where
(plmnt', g) = runState rplmnt (_randGen w)
placePlainPSSpot :: World -> Room -> Placement -> DPoint2 -> ((World,Room),[Placement])
placePlainPSSpot w rm plmnt shift =
let (i,w') = placeSpotID (shiftPSBy shift (_plSpot plmnt)) (_plType plmnt) w
newplmnt = plmnt & plMID ?~ i
in maybe ((w',rm),[newplmnt]) (recrPlace newplmnt w') (_plIDCont plmnt w' newplmnt)
where
recrPlace newplmnt w' pl = let (wr,newplmnts) = placeSpot (w',rm) pl
in (wr,newplmnt:newplmnts)
-- this should be tidied up
placeSpotUsingLink :: World
-> Room
-> Placement
-> (RoomPos -> Room -> Maybe (PlacementSpot,RoomPos))
-> (RoomPos -> Room -> Room)
-> Maybe Placement
-> ((World, Room), [Placement])
placeSpotUsingLink w rm plmnt extract eff fallback = case searchedPoss (_rmPos rm) of
Just (ps,rmposs) -> placeSpot (w, eff (head rmposs) $ rm & rmPos .~ rmposs) (plmnt & plSpot .~ ps)
Nothing -> case fallback of
Nothing -> ((w,rm),[plmnt])
Just plmnt' -> placeSpot (w,rm) plmnt'
where
searchedPoss [] = Nothing
searchedPoss (pos:poss) = case extract pos rm of
Nothing -> second (pos:) <$> searchedPoss poss
Just (ps,rmpos) -> Just ( ps,rmpos:poss)
placeSpotRoomRand :: Room -> Int -> (DPoint2 -> PlacementSpot)
-> Placement -> World -> ((World,Room),[Placement])
placeSpotRoomRand rm i f plmnt w =
let (ps,g) = runState (_rmRandPSs rm !! i) $_randGen w
in placeSpot (w & randGen .~ g,rm) (plmnt & plSpot .~ f ps)
-- the Int here is some id that is assigned when the placement is placed
placeSpotID :: PlacementSpot -> PSType -> World -> (Int, World)
placeSpotID ps pt w = case pt of
PutTrigger cnd -> plNewID triggers cnd w
PutMod mdi -> plNewUpID modifications mdID mdi w
PutProp prp -> plNewUpID props pjID (mvProp p rot prp) w
PutButton bt -> plNewUpID buttons btID (mvButton p rot bt) w
PutTerminal tm -> plNewUpID terminals tmID tm w
PutFlIt itm -> plNewUpID floorItems flItID (createFlIt p rot itm) w
PutCrit cr -> plNewUpID creatures crID (mvCr p rot cr) w
PutForeground fs -> plNewUpID shapes fsID (mvFS p rot fs) w
PutMachine pps mc -> plMachine (map doShift pps) mc p rot w
PutLS ls -> plNewUpID lightSources lsID (mvLS p' rot ls) w
PutPPlate pp -> plNewUpID pressPlates ppID (mvPP p rot pp) w
RandPS rgn -> evaluateRandPS rgn ps w
PutDoor col f pss -> plDoor col f (map (bimap doShift doShift) pss) w
PutCoord cp -> plNewID coordinates (doShift cp) w
PutSlideDr wl dr off a b
-> plSlideDoor wl dr off (doShift a) (doShift b) w
PutBlock bl wl ps' -> plBlock (map doShift ps') (bl & blPos %~ doShift & blDir .~ rot)
wl w
PutLineBlock wl wdth a b -> plLineBlock wl wdth (doShift a) (doShift b) w
PutWall qs wl -> (0,placeWallPoly (map doShift qs) wl w)
PutNothing -> (0,w)
PutID i -> (i, w)
PutWorldUpdate f -> (0, w & f ps)
PutUsingGenParams f -> let (w',pt') = f w
in placeSpotID ps pt' w'
where
p@(V2 px py) = _psPos ps
p' = V3 px py 0
rot = _psRot ps
doShift = shiftPointBy (p,rot)
evaluateRandPS :: State StdGen PSType -> PlacementSpot -> World -> (Int,World)
evaluateRandPS rgen ps w = placeSpotID ps evaluatedType (set randGen g w)
where
(evaluatedType, g) = runState rgen (_randGen w)
placeWallPoly :: [Point2] -> Wall -> World -> World
<<<<<<< HEAD
--placeWallPoly ps wl = over walls (placeWalls ps wl)
placeWallPoly ps wl = rmCrossPaths . over walls (placeWalls ps wl)
where
rmCrossPaths w = foldr (uncurry (addObstacleCrossing' WallObstacle)) w $ loopPairs ps
=======
placeWallPoly ps wl = -- rmCrossPaths .
over walls (placeWalls ps wl)
-- where
-- rmCrossPaths w = foldr (uncurry obstructPathsCrossing) w $ loopPairs ps
>>>>>>> efficientRuntime
placeWalls :: [Point2] -> Wall -> IM.IntMap Wall -> IM.IntMap Wall
placeWalls qs wl wls = foldl' (addPane wl) wls pairs
where
(p:ps) = orderPolygon qs
pairs = zip (ps ++ [p]) (p:ps)
addPane :: Wall -> IM.IntMap Wall -> (Point2,Point2) -> IM.IntMap Wall
addPane wl wls l = IM.insert wlid (wl { _wlLine = l, _wlID = wlid }) wls
where
wlid = IM.newKey wls
---- | place an new object into an intmap and update its id
--plNewUpID :: ALens' World (IM.IntMap a)
-- -> ALens' a Int
-- -> a
-- -> World
-- -> (Int,World)
--plNewUpID l li x w = (i,w & l #%~ IM.insert i (x & li #~ i))
-- where
-- i = IM.newKey $ w ^# l
mvProp :: Point2 -> Float -> Prop -> Prop
mvProp p a = (pjRot +~ a) . (prPos %~ ( (p +.+) . rotateV a ))
mvButton :: Point2 -> Float -> Button -> Button
mvButton p a = (btRot +~ a) . (btPos %~ ( (p +.+) . rotateV a ))
{- Creates a floor item at a given point.-}
createFlIt :: Point2 -> Float -> Item -> FloorItem
createFlIt p rot itm = FlIt { _flItPos = p , _flItRot = rot , _flItID = 0 , _flIt = itm }
mvPP :: Point2 -> Float -> PressPlate -> PressPlate
mvPP p rot pp = pp {_ppPos = p,_ppRot = rot}
mvCr :: Point2 -> Float -> Creature -> Creature
mvCr p rot cr = cr {_crPos = p,_crOldPos = p,_crDir = rot}
mvFS :: Point2 -> Float -> ForegroundShape -> ForegroundShape
mvFS p a = (fsDir +~ a) . (fsPos %~ ( (p +.+) . rotateV a ))
plMachine :: [Point2] -> Machine -> Point2 -> Float -> World -> (Int,World)
plMachine wallpoly mc p rot gw = (mcid
, gw & machines %~ addMc
& walls %~ placeMachineWalls color wallpoly mcid wlid
)
where
color = _mcColor mc
w' = gw
mcid = IM.newKey $ _machines w'
wlid = IM.newKey $ _walls w'
wlids = IS.fromList [wlid .. wlid + length wallpoly - 1]
--addMc = IM.insert mcid (mc {_mcPos = centroid wallpoly,_mcDir = rot,_mcID = mcid, _mcWallIDs = wlids})
addMc = IM.insert mcid (mc {_mcPos = p,_mcDir = rot,_mcID = mcid, _mcWallIDs = wlids})
-- TODO correctly remove/shift pathfinding lines (removePathsCrossing)
placeMachineWalls :: Color -> [Point2] -> Int -> Int -> IM.IntMap Wall -> IM.IntMap Wall
placeMachineWalls col poly mcid wlid = flip (foldr f) $ zip [wlid..] $ loopPairs poly
where
f (wid,l) = IM.insert wid baseWall{_wlID = wid, _wlLine = l}
baseWall = defaultMachineWall
& wlColor .~ col
& wlStructure . wsMachine .~ mcid
& wlTouchThrough .~ True
mvLS :: Point3 -> Float -> LightSource -> LightSource
mvLS (V3 x y z) rot ls = ls & lsParam . lsPos .~ V3 x y z +.+.+ startPos
& lsDir .~ rot
where
startPos = onXY (rotateV rot) $ _lsPos (_lsParam ls)
+126
View File
@@ -0,0 +1,126 @@
{- | Creation, update and destruction of destructible walls. -}
module Dodge.Placement.PlaceSpot.Block
( plBlock
, plLineBlock
)
where
import Dodge.Data
import Dodge.Path
import Dodge.Block.Debris
--import Dodge.LevelGen.LevelStructure
import Dodge.Base
--import Dodge.Zone
import Geometry
import qualified IntMapHelp as IM
import Control.Lens
import qualified Data.IntSet as IS
plBlock
:: [Point2] -- ^ Block polygon
-> Block
-> Wall -- ^ Base Pane
-> World
-> (Int,World)
plBlock [] _ _ _ = error "Trying to add a block with incomplete polygon"
plBlock (p:ps) bl wl w = (,) blid $ w
<<<<<<< HEAD
& flip (foldr insertWall) wls
=======
>>>>>>> efficientRuntime
& blocks . at blid ?~ bl
{ _blID = blid
, _blWallIDs = IS.fromList is
, _blShadows = []
, _blFootprint = p:ps
}
<<<<<<< HEAD
=======
& insertWalls blid wls
>>>>>>> efficientRuntime
where
blid = IM.newKey $ _blocks w
lns = zip (p:ps) (ps ++ [p])
i = IM.newKey $ _walls w
is = [i.. i + length lns-1]
wls = zipWith
(\j ln -> wl & wlLine .~ ln & wlID .~ j & wlStructure .~ BlockPart blid)
is
lns
{- | Splits a line into many four cornered blocks. -}
plLineBlock
:: Wall -- ^ Base pane
-> Float
-> Point2 -- ^ Start point (symmetric)
-> Point2 -- ^ End point (symmetric)
-> World
-> (Int, World)
plLineBlock basePane blwidth a b gw = ( 0
<<<<<<< HEAD
, foldr insertWall (insertBlocks gw) listWalls
=======
-- , foldr insertWall (insertBlocks gw) listWalls
, insertBlocks gw
>>>>>>> efficientRuntime
)
where
depth = blwidth
psOnLine = divideLineOddNumPoints blwidth a b
halfBlockWidth = dist a b / fromIntegral (length psOnLine - 1)
blockCenPs = snd $ evenOddSplit psOnLine
numBlocks = length blockCenPs
is = [0.. numBlocks - 1]
cornerPoints = reverse $ rectWH halfBlockWidth depth -- goes clockwise around the block
cornersAt p = fmap ( (p +.+) . rotateV (argV (b -.- a)) ) cornerPoints
linesAt p = loopPairs $ cornersAt p
wlid = IM.newKey $ _walls gw
blid = IM.newKey $ _blocks gw
insertBlock (i,p) =
insertWalls (i + blid) (makeWallAt p i)
. (over blocks $ IM.insert (i+blid) Block
{ _blID = i + blid, _blWallIDs = IS.fromList $ ksAtI i
, _blHP = 1000, _blShadows = shadowsAt i
, _blDir = 0 -- THIS IS NOT SENSIBLE. TODO rethink block positioning
, _blFootprint = cornersAt p -- TODO check winding (clockwise, anticlockwise)
, _blObstructs = []
<<<<<<< HEAD
=======
, _blPaths = []
>>>>>>> efficientRuntime
, _blPos = p, _blDraw = const mempty , _blDeath = makeBlockDebris}
)
insertBlocks = flip (foldr insertBlock) $ zip is blockCenPs
ksAtI i = map ( + (wlid + i*4) ) [0,1,2,3]
visibilityAt i
| i == 0 = [False,True,True ,True]
| i == numBlocks - 1 = [True ,True,False,True]
| otherwise = [False,True,False,True]
shadowsAt i
| i == 0 = ksAtI 1
| i == numBlocks - 1 = ksAtI $ numBlocks - 2
| otherwise = ksAtI (i-1) ++ ksAtI (i+1)
makeWallAt p i = zipWith3 (makePane i) (visibilityAt i) (ksAtI i) (linesAt p)
makePane i visStatus k ps = basePane
{_wlID = k
,_wlStructure = BlockPart $ i + blid
,_wlLine = ps
,_wlDraw = visStatus
}
listWalls = concat $ zipWith makeWallAt blockCenPs is
<<<<<<< HEAD
insertWall :: Wall -> World -> World
insertWall wl = (walls . at (_wlID wl) ?~ wl)
. uncurry (addObstacleCrossing' BlockObstacle) (_wlLine wl)
=======
-- | Must be done after inserting the block
insertWalls :: Int -> [Wall] -> World -> World
insertWalls blid wls w = w' & blocks . ix blid . blPaths .~ concat paths
where
(w',paths) = mapAccumR (flip insertWall) w wls
insertWall :: Wall -> World -> (World,[(Int,Int,PathEdge)])
insertWall wl = uncurry obstructPathsCrossing (_wlLine wl)
. (walls . at (_wlID wl) ?~ wl)
>>>>>>> efficientRuntime
+1 -1
View File
@@ -8,6 +8,6 @@ randFirstWeapon :: State StdGen Item
randFirstWeapon = takeOne $
replicate 10 pistol
++ replicate 5 (bangStick 4)
++ replicate 5 (bangCaneX 3)
++ replicate 5 (volleyGun 3)
++ replicate 5 bangCone
++ [lasGun]